/
trilirium
/
Archived_BLC
Обзор
Документация
Войти
/
trilirium
/
Archived_BLC
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Container.cpp
2 490 строк
50 KB
Trilirium
repo created OK
22 янв 2026, 16:43
22 янв 2026, 16:43
f05d14b
Код
Авторство
О чём код?
/* + === - === - === - === - === - === - === - === | | Container.cpp: | | Data container(s) / multicontainer(s) | (defined / implemented) | + === - === - === - === - === - === - === - === */ #include <cstring> #include <io.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/utime.h> #ifndef CONTAINER_H #include "mem_mgr.h" #include "Console.h" #endif #include "CharCodec.cpp" // // Abstract streaming interfaces // // Abstract: Output stream struct A_Output { // (write raw data block: returns # of bytes written) virtual int write (char const *data, unsigned count) = 0; // (write character) virtual bool put_byte (char ch) { return write (&ch, 1) == 1; } // (write unsigned value) virtual bool put_value (unsigned value) = 0; }; // A_Output // Abstract: Input stream struct A_Input { // (read raw data block: returns # of bytes read) virtual int read (char *data, unsigned count) = 0; // (read character) virtual bool get_byte (char &ch) { return read (&ch, 1) == 1; } // (read unsigned value) virtual bool get_value (unsigned &value) = 0; }; // A_Input // Abstract: Input/output stream struct A_InOut : A_Input, A_Output { // (seek to offset) virtual bool seek (size_t offset) = 0; // (tell offset) virtual bool tell (size_t &offset) = 0; // (read data block, [count] from self -> to 'output'. // Returns true on success.) bool read_to (A_Output &output, unsigned count); // (write data block, [count] to self <- from 'input'. // Returns true on success.) bool write_from (A_Input &input, unsigned count); }; // A_InOut // Abstract: file system operations struct A_System { // get file size virtual bool file_size (char const *file_name, unsigned &size) = 0; // get file date/time virtual bool file_time (char const *file_name, time_t &time, bool time_flag = true) = 0; // change file date/time // (time_flag ? modify time : access time) virtual bool file_change_time (char const *file_name, time_t change_time, bool time_flag = true) = 0; // check directory presence virtual bool is_dir (char const *dir_name) = 0; // create directory virtual bool create_dir (char const *dir_name) = 0; // change directory virtual bool change_dir (char const *dir_name) = 0; // remove directory virtual bool remove_dir (char const *dir_name) = 0; }; // A_System // // Container (of tagged content) // struct C_Value { unsigned c_length; // value content length char const *c_data; // value content data bool used () { return c_length && c_data; } }; // C_Value // // Input content from fixed buffer // // Abstract: Input stream struct B_Input : A_Input { char *base, *limit; // (constructor) B_Input (char *base, unsigned size) { limit = (this->base = base) + size; } // (read raw data block: returns # of bytes read) int read (char *data, unsigned count) { // (sanity check!) if (base + count > limit) count = limit - base; memcpy (data, base, count); base += count; return count; } // read // (read character) bool get_byte (char &ch) { // (sanity check!) if (base < limit) { ch = * base ++; return true; } return false; // (no data) } // get_byte // (read unsigned value) bool get_value (unsigned &value) { return read ((char *) &value, sizeof (value)) == sizeof (value); } // get_value }; // B_Input // // Output content to fixed buffer // // Abstract: Output stream struct B_Output : A_Output { char *base, *limit; // (constructor) B_Output (char *base, unsigned size) { limit = (this->base = base) + size; } // (write raw data block: returns # of bytes written) int write (char const *data, unsigned count) { // (sanity check!) if (base + count > limit) count = limit - base; memcpy (base, data, count); base += count; return count; } // write // (write character) bool put_byte (char ch) { // (sanity check!) if (base < limit) { * base ++ = ch; return true; } return false; // (no room) } // put_byte // (write unsigned value) bool put_value (unsigned value) { return write ((char const *) &value, sizeof (value)) == sizeof (value); } // put_value }; // B_Output // // Container release flags // enum { CRel_Head = 1 << 0, // release head part CRel_Body = 1 << 1, // release body part CRel_Self = 1 << 2 // release self }; struct Container { // (contained item) struct Contained { char const *c_name; // entry name C_Value c_value; // entry content struct Contained *next; // next in chain } * start; // // trivial constructor // Container () { start = 0; } // // read/write tag header // // encoded size of 'name' static unsigned size_head (char const *name); // 'output' <= 'name' (false on error) static bool write_head (A_Output &output, char const *name); // 'input' => 'name' (NULL on error) static char const * read_head (A_Input &input); // // read/write tag content // // encoded size of 'body' static unsigned size_body (C_Value &c_value); // 'output' <= 'data[length]' (false on error) static bool write_body (A_Output &output, C_Value &c_value); // 'input' => 'data[=> length]' (NULL on error) static bool read_body (A_Input &input, C_Value &c_value); // skip tag body in 'input' static bool skip_body (A_Input &input); // add new item to container void add_item (char const *name, unsigned length, char const *data); // add reserved item to container C_Value &add_empty (char const *name); // add trailer block to container C_Value &add_trail_block (char const *block_data, unsigned block_length); // replace (possible existing) item in container // (true, if item replaced) bool replace_item (char const *name, unsigned length, char const *data); // delete item from container // (true, if item replaced) bool delete_item (char const *name); // count container items unsigned count_all (); // total size of container contents unsigned size_all (); // write container contents (=> Output) bool write_all (A_Output &output); // read container contents (<= Input) bool read_all (A_Input &input); // update container contents (<= Input) bool update_all (A_Input &input); // dump container (=> console) void dump_all (A_Console &console); // hash container void hash_all (unsigned hash_Q[3]); // unroll container unsigned unroll_all (char const *tag, char *&content); // reverse content void reverse (); // release container (all or selected elements) unsigned release_all (unsigned release_flags); // Init multi-container static bool init_Multi (A_InOut &inout); // Items in multi-container static bool total_Multi (A_InOut &inout, unsigned &count); // Locate entry in multi-container static char const * locate_Multi (A_InOut &inout, unsigned index); // Locate index range ('inx_start' -> 'inx_end') // in multi-container (to 'off_start' -> 'off_end') static bool range_Multi (A_InOut &inout, unsigned &inx_start, unsigned &inx_end, unsigned &off_start, unsigned &off_end); // Append entry to multi-container (from self) bool append_Multi (A_InOut &inout, char const *entry_name); // Append entry to multi-container (from 'entry_data') static bool append_data_Multi (A_InOut &inout, char const *entry_name, char const *entry_data, unsigned entry_size); // Append entry to multi-container (from 'source') static bool download_Multi (A_InOut &inout, char const *entry_name, unsigned entry_size, A_Input &source); // Concatenate (inject) entries read to multi-container bool inject_Multi (A_InOut &inout, unsigned total, A_Input &source); // Iterate on multi-container static bool iterate_Multi (struct MultiRead_Iterator &iterator); // Hash tag value static void hash_string (char const *source, unsigned length, unsigned hash_Q[3]); }; // Container #ifndef CONTAINER_H #include "hashcalc.cpp" // // add new item to container // void Container::add_item (char const *name, unsigned length, char const *data) { Contained *entry = new ("[C+]") Contained; entry->c_name = name; entry->c_value.c_length = length; entry->c_value.c_data = data; entry->next = start; start = entry; } // Container::add_item // // add reserved (empty) item to container // C_Value &Container::add_empty (char const *name) { Contained *entry = new ("[C-]") Contained; entry->c_name = name; entry->c_value.c_length = 0; entry->c_value.c_data = 0; entry->next = start; start = entry; return entry->c_value; } // Container::add_empty // (special block tag) static char const * block_tag = ""; // // add trailer block to container // C_Value &Container::add_trail_block (char const *block_data, unsigned block_length) { Contained *entry = new ("[C [^]]") Contained; entry->c_name = block_tag; entry->c_value.c_length = block_length; entry->c_value.c_data = block_data; entry->next = start; start = entry; return entry->c_value; } // Container::add_trail_block // // replace (possible existing) item in container // (true, if item replaced) // bool Container::replace_item (char const *name, unsigned length, char const *data) { for (Contained *entry = start; entry; entry = entry->next) { if (entry->c_name && strcmp (entry->c_name, name) == 0) { // (found!!!) if (entry->c_value.c_data) delete [] entry->c_value.c_data; entry->c_value.c_length = length; entry->c_value.c_data = data; return true; } } // for (entry) // (not found:) add_item (name, length, data); return false; } // Container::replace_item // // delete item from container // (true, if item replaced) // bool Container::delete_item (char const *name) { char const * e_name; for (Contained **entry = &start; *entry; entry = &((*entry)->next)) { if ((e_name = (*entry)->c_name) && strcmp (e_name, name) == 0) { // (found!!!) Contained *elem = *entry; *entry = elem->next; if (elem->c_value.c_data) delete [] elem->c_value.c_data; delete elem; return true; } } // for (entry) // (not found) return false; } // Container::delete_item // Count container items unsigned Container::count_all () { unsigned count = 0; for (Contained *entry = start; entry; entry = entry->next) { if (entry->c_value.used ()) ++ count; } // for (entry) return count; } // Container::count_all // Total size of container contents unsigned Container::size_all () { unsigned size = 0; for (Contained *entry = start; entry; entry = entry->next) { if (entry->c_value.used ()) size += size_head (entry->c_name) + size_body (entry->c_value); } // for (entry) return size; } // Container::size_all // Write container contents (=> Output) bool Container::write_all (A_Output &output) { for (Contained *entry = start; entry; entry = entry->next) { if (entry->c_value.used ()) if (write_head (output, entry->c_name) && write_body (output, entry->c_value)); else return false; } // for (entry) return true; } // Container::write_all // Read container contents (<= Input) bool Container::read_all (A_Input &input) { Contained ** tail = &start; char const *c_name; while (c_name = read_head (input)) { Contained *entry = new ("[C*]") Contained; if (read_body (input, entry->c_value)) { entry->c_name = c_name; entry->next = 0; *tail = entry; tail = &entry->next; } else return false; } // (while) return true; } // Container::read_all // Update container contents (<= Input) bool Container::update_all (A_Input &input) { char const *c_name; while (c_name = read_head (input)) { Contained *entry; for (entry = start; entry; entry = entry->next) { if (strcmp (entry->c_name, c_name) == 0) break; } // for (entry) if (entry) { // (entry found!) if (! read_body (input, entry->c_value)) return false; } else // (entry not found...) if (! skip_body (input)) return false; delete c_name; } // (while) return true; } // Container::update_all // Unroll container (to 'content') unsigned Container::unroll_all (char const *tag, char *&content) { unsigned length = size_all (); content = new (tag) char [length]; B_Output _output (content, length); write_all (_output); return length; } // Container::unroll_all // Reverse content void Container::reverse () { Contained *entry = start; start = 0; while (entry) { Contained *_next = entry->next; entry->next = start; start = entry; entry = _next; } } // Container::reverse // Release container // (all or selected elements) unsigned Container::release_all (unsigned release_flags) { bool flag_Head = (release_flags & CRel_Head) != 0, flag_Body = (release_flags & CRel_Body) != 0, flag_Self = (release_flags & CRel_Self) != 0; Contained *entry = start; unsigned count = 0; while (entry) { if (flag_Head) { // (...release Head) delete [] entry->c_name; entry->c_name = 0; } if (flag_Body) { // (...release Body) delete [] entry->c_value.c_data; entry->c_value.c_data = 0; entry->c_value.c_length = 0; } if (flag_Self) { // (...release container node) start = entry->next; delete entry; entry = start; } else entry = entry->next; ++ count; } // while (entry) return count; } // Container::release_all // Hash container void Container::hash_all (unsigned hash_Q[3]) { Hash_Accum accumulator; unsigned count = 0; accumulator.init (); for (Contained *entry = start; entry; entry = entry->next) { accumulator.append (entry->c_name, strlen (entry->c_name) + 1); if (entry->c_value.used ()) accumulator.append (entry->c_value.c_data, entry->c_value.c_length); ++ count; } // for (entry) accumulator.append (&count, sizeof (count)); accumulator.term (hash_Q); } // Container::hash_all // Hash tag value void Container::hash_string (char const *source, unsigned length, unsigned hash_Q[3]) { Hash_Accum accumulator; accumulator.init (); accumulator.append (source, length); accumulator.append (&length, sizeof (length)); accumulator.term (hash_Q); } // Container::hash_string // Dump container (to console) void Container::dump_all (A_Console &console) { for (Contained *entry = start; entry; entry = entry->next) { console. out_cstr (entry->c_name). out_nl (); if (entry->c_value.used ()) { console. out_ch ('\t'). out_text (entry->c_value.c_data, entry->c_value.c_length). out_nl (); } } // for (entry) } // Container::dump_all // Size of tag body unsigned Container::size_body (C_Value &value) { unsigned length = value.c_length; return ( length < 0x80u ? 1 : length < 0x4000u ? 2 : length < 0x200000u ? 3 : length < 0x10000000u ? 4 : 0) + length; } // Container::size_body // Write tag body: // 'output' <= 'data[length]' (return false on error) bool Container::write_body (A_Output &output, C_Value &value) { unsigned length = value.c_length; unsigned index = 0; char prefix [4]; if (length < 0x80u) prefix [index ++] = length << 1; else if (length < 0x4000u) { prefix [index ++] = (length << 2) | 1; prefix [index ++] = (length >> 6); } else if (length < 0x200000u) { prefix [index ++] = (length << 3) | 3; prefix [index ++] = (length >> 5); prefix [index ++] = (length >> 13); } else if (length < 0x10000000u) { prefix [index ++] = (length << 4) | 7; prefix [index ++] = (length >> 4); prefix [index ++] = (length >> 12); prefix [index ++] = (length >> 20); } else return false; return output.write (prefix, index) == index && output.write (value.c_data, length) == length; } // Container::write_body // Read tag body: 'input' => 'data[=> length]' (return NULL on error) bool Container::read_body (A_Input &input, C_Value &value) { char prefix [4], start; if (input.read (prefix, 1) != 1) return false; start = ~ *prefix; unsigned prelen = (start & 1) ? 1 : (start & 2) ? 2 : (start & 4) ? 3 : (start & 8) ? 4 : 0; if (prelen > 1) if (input.read (prefix + 1, prelen - 1) != prelen - 1) return false; unsigned count = 0; char *ptr = (char *) (prefix + prelen); while (ptr != prefix) count = (count << 8) | (* -- ptr & 0xFF); count >>= prelen; char * content = new ("<C.body>") char [count]; if (content) { if (input.read (content, count) == count) { value.c_length = count; value.c_data = content; return true; } // (read failed!) delete content; } return false; } // Container::read_body // Skip tag body: 'input' => NULL bool Container::skip_body (A_Input &input) { C_Value value; return read_body (input, value); } // Container::skip_body // Tag name length limit: enum { MaxNameLen = 16 }; // Size: 'name' unsigned Container::size_head (char const *name) { // if (name == block_tag) // return 0; return strlen (name) + 1; } // Container::size_head // Write: 'output' <= 'name' (false on error) bool Container::write_head (A_Output &output, char const *name) { // if (name == block_tag) return true; unsigned name_len = strlen (name); if (name_len && name_len <= MaxNameLen) { output.put_byte (0x100 - name_len); output.write (name, name_len); return true; } return false; } // Container::write_head // Read: 'input' => 'name' (NULL on error) char const * Container::read_head (A_Input &input) { char prefix; unsigned name_len; if (input.get_byte (prefix) && (prefix & 0xF0) == 0xF0) { unsigned name_len = MaxNameLen - (prefix & 0x0F); char * name = new ("<C.head>") char [name_len + 1]; if (name) { if (input.read (name, name_len) == name_len) { name [name_len] = 0; return name; } // (read failed) delete name; } } return 0; // (failure) } // Container::read_head #endif // Container values save/restore // Free container value void CFreeVal (C_Value &value); // Check container value for presence bool CTestVal (C_Value &value); // Compare value in container bool CCompVal (C_Value &left, char const *s_right); // Get value length unsigned CLengthVal (C_Value &value); // Save container value char *CSaveVal (char const *tag, C_Value &value); // Save container data value void CSaveDataVal (C_Value &value, void *block, unsigned len); // Load container value void CLoadVal (C_Value &value, char const *string); // Load container data value void CLoadDataVal (C_Value &value, void const *block, unsigned len); #ifndef CONTAINER_H // Free container value void CFreeVal (C_Value &value) { delete value.c_data; value.c_data = 0; value.c_length = 0; } // CFreeVal // Check container value for presence bool CTestVal (C_Value &value) { return value.c_data != 0; } // CTestVal // Compare value in container bool CCompVal (C_Value &left, char const *s_right) { unsigned length; if ((length = left.c_length) && strlen (s_right) == length && memcmp (left.c_data, s_right, length) == 0 ) return true; return false; } // CCompVal // Get length unsigned CLengthVal (C_Value &value) { return value.c_length; } // Save container value char *CSaveVal (char const *tag, C_Value &value) { unsigned length; if (length = value.c_length) { char *result = new (tag) char [length + 1]; memcpy (result, value.c_data, length); result [length] = '\0'; return result; } return 0; } // CSaveVal // Save container data value void CSaveDataVal (C_Value &value, void *block, unsigned len) { if (value.c_length == len) memcpy (block, value.c_data, len); // (else??) } // CSaveDataVal // Load container value void CLoadVal (C_Value &value, char const *string) { value.c_length = strlen (string); value.c_data = string; } // CLoadVal // Load container data value void CLoadDataVal (C_Value &value, void const *block, unsigned len) { value.c_length = len; value.c_data = (char const *) block; } // CLoadDataVal // // Multi-container // // (MC signatures) enum MC_Enum { MC_Sign0 = 0x94F1564C, MC_Sign1 = 0x84C1E877, MC_Sign2 = 0x338FFAC6 }; // MC_Enum // Init multi-container bool Container::init_Multi (A_InOut &inout) { // // (write MC head) // if (! inout.put_value (MC_Sign0)) return false; unsigned offset = sizeof (unsigned) + sizeof (offset) + sizeof (unsigned); if (! inout.put_value (offset)) return false; if (! inout.put_value (MC_Sign1)) return false; // (initial content: empty) // // (write MC tail) // if (! inout.put_value (MC_Sign2)) return false; unsigned index = 0; if (! inout.put_value (index)) return false; if (! inout.put_value (MC_Sign0)) return false; return true; } // Container::init_Multi // Count items in multi-container bool Container::total_Multi (A_InOut &inout, unsigned &count) { unsigned Sign; unsigned offset; unsigned total; // // check MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; if (! inout.get_value (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign1) return false; // // check MC tail: // if (! inout.seek (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign2) return false; if (! inout.get_value (total)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; inout.seek (0); count = total; return true; } // Container::total_Multi // Locate entry in multi-container // (by index) char const * Container::locate_Multi (A_InOut &inout, unsigned index) { unsigned Sign; unsigned final_offset; unsigned total; // // check MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return 0; if (! inout.get_value (final_offset)) return 0; if (! inout.get_value (Sign) || Sign != MC_Sign1) return 0; // // check MC tail: // if (! inout.seek (final_offset)) return 0; if (! inout.get_value (Sign) || Sign != MC_Sign2) return 0; if (! inout.get_value (total)) return 0; if (! inout.get_value (Sign) || Sign != MC_Sign0) return 0; unsigned offset = sizeof (Sign) + sizeof (final_offset) + sizeof (Sign); ++ index; while (offset < final_offset) { unsigned entry_index, entry_size; char const * entry_name; inout.seek (offset); if ((entry_name = read_head (inout)) && inout.get_value (entry_index) && inout.get_value (entry_size)); else // (something failed...) return 0; if (! -- index) // (target entry found!) return entry_name; // more entries to skip: offset += size_head (entry_name) + sizeof (entry_index) + sizeof (entry_size) + entry_size + 1; delete entry_name; } // (while) // end of data: return 0; } // Container::locate_Multi // Locate index range ('inx_start' -> 'inx_end') // in multi-container (to 'off_start' -> 'off_end') bool Container::range_Multi (A_InOut &inout, unsigned &inx_start, unsigned &inx_end, unsigned &off_start, unsigned &off_end) { unsigned Sign; unsigned final_offset; unsigned total; // // check MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return 0; if (! inout.get_value (final_offset)) return 0; if (! inout.get_value (Sign) || Sign != MC_Sign1) return 0; // // check MC tail: // if (! inout.seek (final_offset)) return 0; if (! inout.get_value (Sign) || Sign != MC_Sign2) return 0; if (! inout.get_value (total)) return 0; if (! inout.get_value (Sign) || Sign != MC_Sign0) return 0; unsigned offset = sizeof (Sign) + sizeof (final_offset) + sizeof (Sign); unsigned start = inx_start, end = inx_end; off_start = off_end = 0; if (start < end) end -= start; else end = 0; while (offset < final_offset) { unsigned entry_index, entry_size; char const * entry_name; inout.seek (offset); if ((entry_name = read_head (inout)) && inout.get_value (entry_index) && inout.get_value (entry_size)); else // (something failed...) return false; if (! start) { if (! off_start) off_start = offset; if (! end) { off_end = offset; return true; } else -- end; } else -- start; offset += size_head (entry_name) + sizeof (entry_index) + sizeof (entry_size) + entry_size + 1; // more entries to skip: delete entry_name; } // (while) // end of data: return false; } // Container::range_Multi // Append to multi-container bool Container::append_Multi (A_InOut &inout, char const *entry_name) { unsigned Sign; unsigned offset; unsigned index; // // check MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; if (! inout.get_value (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign1) return false; // // check MC tail: // if (! inout.seek (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign2) return false; if (! inout.get_value (index)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; // append entry: if (! inout.seek (offset)) return false; if (! write_head (inout, entry_name)) return false; unsigned total_size = size_all (); if (! inout.put_value (index) || ! inout.put_value (total_size)) return false; if (! write_all (inout)) return false; if (! inout.put_byte (0)) return false; // // rewrite head && tail: // index ++; if (! inout.put_value (MC_Sign2)) return false; if (! inout.put_value (index)) return false; if (! inout.put_value (MC_Sign0)) return false; if (! inout.seek (sizeof (Sign))) return false; offset += size_head (entry_name) + sizeof (index) + sizeof (total_size) + total_size + 1; if (! inout.put_value (offset)) return false; return true; } // Container::append_Multi // Append entry to multi-container (from 'entry_data') bool Container::append_data_Multi (A_InOut &inout, char const *entry_name, char const *entry_data, unsigned entry_size) { unsigned Sign; unsigned offset; unsigned index; // // check MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; if (! inout.get_value (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign1) return false; // // check MC tail: // if (! inout.seek (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign2) return false; if (! inout.get_value (index)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; // append entry: if (! inout.seek (offset)) return false; if (! write_head (inout, entry_name)) return false; if (! inout.put_value (index) || ! inout.put_value (entry_size)) return false; if (! inout.write (entry_data, entry_size)) return false; if (! inout.put_byte (0)) return false; // // rewrite head && tail: // index ++; if (! inout.put_value (MC_Sign2)) return false; if (! inout.put_value (index)) return false; if (! inout.put_value (MC_Sign0)) return false; if (! inout.seek (sizeof (Sign))) return false; offset += size_head (entry_name) + sizeof (index) + sizeof (entry_size) + entry_size + 1; if (! inout.put_value (offset)) return false; return true; } // Container::append_data_Multi // Append entry to multi-container (from 'source') bool Container::download_Multi (A_InOut &inout, char const *entry_name, unsigned entry_size, A_Input &source) { unsigned Sign; unsigned offset; unsigned index; // // check MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; if (! inout.get_value (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign1) return false; // // check MC tail: // if (! inout.seek (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign2) return false; if (! inout.get_value (index)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; // append entry: if (! inout.seek (offset)) return false; if (! write_head (inout, entry_name)) return false; if (! inout.put_value (index) || ! inout.put_value (entry_size)) return false; if (! inout.write_from (source, entry_size)) return false; if (! inout.put_byte (0)) return false; // // rewrite head && tail: // index ++; if (! inout.put_value (MC_Sign2)) return false; if (! inout.put_value (index)) return false; if (! inout.put_value (MC_Sign0)) return false; if (! inout.seek (sizeof (Sign))) return false; offset += size_head (entry_name) + sizeof (index) + sizeof (entry_size) + entry_size + 1; if (! inout.put_value (offset)) return false; return true; } // Container::download_Multi // Concatenate (inject) entries read to multi-container bool Container::inject_Multi (A_InOut &inout, unsigned total, A_Input &source) { unsigned Sign; unsigned offset; unsigned index; // // check MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; if (! inout.get_value (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign1) return false; // // check MC tail: // if (! inout.seek (offset)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign2) return false; if (! inout.get_value (index)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; // // append 'total' new entries: // while (total) { unsigned entry_index, entry_size; char const * entry_name; if ((entry_name = read_head (source)) && source.get_value (entry_index) && source.get_value (entry_size)) { // TODO: check return values !!! write_head (inout, entry_name); inout.put_value (index ++); inout.put_value (entry_size); inout.write_from (source, entry_size); if (! inout.put_byte (0)) return false; offset += size_head (entry_name) + sizeof (index) + sizeof (entry_size) + entry_size + 1; -- total; } else break; } // while (total) // // rewrite head && tail: // if (! inout.put_value (MC_Sign2)) return false; if (! inout.put_value (index)) return false; if (! inout.put_value (MC_Sign0)) return false; if (! inout.seek (sizeof (Sign))) return false; if (! inout.put_value (offset)) return false; return true; } // Container::inject_Multi #endif // // Iterate on multi-container // struct MultiRead_Iterator { A_InOut &_inout; // // constructor // MultiRead_Iterator (A_InOut &inout) : _inout (inout) {} // // (virtuals) // // (On processing begin:) virtual bool on_begin (unsigned offset_final) { return true; } // (On container entry:) virtual bool on_entry (char const * entry_name, unsigned entry_index, unsigned entry_size) { return true; } // (On processing final:) virtual bool on_final (unsigned total_count) { return true; } }; // MultiRead_Iterator #ifndef CONTAINER_H // Iterate on multi-container bool Container::iterate_Multi (MultiRead_Iterator &iterator) { unsigned Sign; unsigned offset = sizeof (unsigned) + sizeof (offset) + sizeof (unsigned); unsigned final_off; A_InOut &inout (iterator._inout); // // check for MC head: // if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; if (! inout.get_value (final_off)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign1) return false; if (! iterator.on_begin (final_off)) return false; // // iterate on entries: // while (offset < final_off) { unsigned entry_index, entry_size; char const * entry_name = read_head (inout); if (entry_name && inout.get_value (entry_index) && inout.get_value (entry_size)); else // (something failed...) return false; // (call on entry) if (! iterator.on_entry (entry_name, entry_index, entry_size)) return false; offset += size_head (entry_name) + sizeof (entry_index) + sizeof (entry_size) + entry_size + 1; delete entry_name; inout.seek (offset); } // (while) // // check for MC tail: // if (offset == final_off) { unsigned last_index; if (! inout.get_value (Sign) || Sign != MC_Sign2) return false; if (! inout.get_value (last_index)) return false; if (! inout.get_value (Sign) || Sign != MC_Sign0) return false; if (! iterator.on_final (last_index)) return false; return true; // (everything OK) } return false; } // Container::iterate_Multi #endif // // Multi-container copy iterator // struct MultiCopy_Iterator : MultiRead_Iterator { A_InOut &_output; unsigned actual_count; // // constructor // MultiCopy_Iterator (A_InOut &inout, A_InOut &output) : MultiRead_Iterator (inout), _output (output) {} // // (virtuals) // // (On processing begin:) bool on_begin (unsigned offset_final); // (On container entry:) bool on_entry (char const * entry_name, unsigned entry_index, unsigned entry_size); // (On processing final:) bool on_final (unsigned total_count); // Insert entry 'content' in 'output' (with 'content_name' && 'content_index') bool insert_content (char const * content_name, unsigned content_index, Container &content); // Skip entry body in input bool skip_entry (unsigned entry_size); }; // MultiCopy_Iterator #ifndef CONTAINER_H // Insert entry 'content' in 'output' (with 'content_name' && 'content_index') bool MultiCopy_Iterator::insert_content (char const * content_name, unsigned content_index, Container &content) { unsigned total_size = content.size_all (); if (Container::write_head (_output, content_name) && _output.put_value (content_index) && _output.put_value (total_size) ) { ++ actual_count; return content.write_all (_output) && _output.put_byte (0); } // (something failed!) return false; } // MultiCopy_Iterator::insert_content // Skip entry body in input bool MultiCopy_Iterator::skip_entry (unsigned entry_size) { unsigned offset; return _inout.tell (offset) && _inout.seek (offset + entry_size + 1); } // MultiCopy_Iterator::skip_entry // (On processing begin:) bool MultiCopy_Iterator::on_begin (unsigned offset_final) { // // (write MC head) // if (! _output.put_value (MC_Sign0)) return false; unsigned offset = sizeof (unsigned) + sizeof (offset) + sizeof (unsigned); if (! _output.put_value (offset)) return false; if (! _output.put_value (MC_Sign1)) return false; actual_count = 0; // (nothing written!) return true; } // MultiCopy_Iterator::on_begin // (On container entry:) bool MultiCopy_Iterator::on_entry (char const * entry_name, unsigned entry_index, unsigned entry_size) { if (Container::write_head (_output, entry_name) && _output.put_value (entry_index) && _output.put_value (entry_size) ) { ++ actual_count; return _output.write_from (_inout, entry_size + 1); } // (something failed!) return false; } // MultiCopy_Iterator::on_entry // (On processing final:) bool MultiCopy_Iterator::on_final (unsigned total_count) { unsigned Sign; // // rewrite head && tail: // unsigned offset; if (! _output.tell (offset)) return false; if (! _output.put_value (MC_Sign2)) return false; if (! _output.put_value (actual_count)) return false; if (! _output.put_value (MC_Sign0)) return false; if (! _output.seek (sizeof (Sign))) return false; if (! _output.put_value (offset)) return false; return true; } // MultiCopy_Iterator::on_final #endif // // Meta tags lookup // struct Def_CTag { unsigned tag_value; char const *tag_name; // look for tag name in array (sequental) unsigned lookup (char const *tag_name, bool cleanup) { Def_CTag *def; if (tag_name) { for (def = this; def->tag_name; def ++) if (strcmp (tag_name, def->tag_name) == 0) { // (found!) if (cleanup) delete tag_name; return def->tag_value; } // (not found) return def ? def->tag_value : 0; } // (tag_name) return 0; } // lookup // look for tag value in array (sequental) char const *lookup (unsigned tag_value, bool cleanup) { Def_CTag *def; for (def = this; def->tag_name; def ++) if (tag_value == def->tag_value) { // (found!) return def->tag_name; } // (not found) return def ? def->tag_name : 0; } // lookup }; // Def_CTag // // File system Input/Output // #include <fcntl.h> // // Create file name (with path) // static char const * path_name (char const * name, char const * path) { if (name) { unsigned nlen = strlen (name); if (path) { unsigned plen = strlen (path); char *result = new ("<path+file>") char [plen + 1 + nlen + 1]; strcpy (result, path); result [plen] = '/'; strcpy (result + plen + 1, name); return result; } else { char *result = new ("<file>") char [nlen + 1]; strcpy (result, name); return result; } } // (name) return 0; } // path_name static char const * file_name (char const * name) { return path_name (name, 0); } // file_name // // Create file name (with path and directory) // static char const * path_dir_name (char const * name, char const * dir, char const * path) { if (name) { unsigned nlen = strlen (name); if (path) { unsigned plen = strlen (path); unsigned dlen = dir ? strlen (dir) : 0; char *result = new ("<path+dir+file>") char [plen + 1 + (dlen ? dlen + 1 : 0) + nlen + 1]; strcpy (result, path); result [plen] = '/'; if (dlen) { strcpy (result + plen + 1, dir); plen += dlen + 1; result [plen] = '/'; } strcpy (result + plen + 1, name); return result; } else { char *result = new ("<file>") char [nlen + 1]; strcpy (result, name); return result; } } // (name) return 0; } // path_dir_name // // Release file name: // static void free_name (char const *pointer) { if (pointer) delete [] pointer; } // free_name // // Path+subdir record // struct PathDir { char const *path; char const *dir; PathDir () { path = dir = 0; } void dump (A_Console &logger) { logger.out_cstr (path). out_cstr (" :: "). out_cstr (dir); } }; // PathDir // // File output implemented // struct File_Output : A_Output { char const * filename; int fd; // (constructor) File_Output (int _fd) { fd = _fd; filename = 0; } // File_Output // (constructor) File_Output (char const *name) { fd = -1; filename = file_name (name); } // File_Output // (constructor) File_Output (char const *name, char const *path) { fd = -1; filename = path_name (name, path); } // File_Output // (constructor) File_Output (char const *name, char const *dir, char const *path) { fd = -1; filename = path_dir_name (name, dir, path); } // File_Output // (constructor) File_Output (char const *name, PathDir &path_dir) { fd = -1; filename = path_dir_name (name, path_dir.dir, path_dir.path); } // File_Output // (open output file) bool open () { fd = ::open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0700); return fd >= 0; } // (write data block: returns # of bytes written) int write (char const *data, unsigned count) { return ::write (fd, data, count); } // (write unsigned value) bool put_value (unsigned value) { return write ((char const *) &value, sizeof (value)) == sizeof (value); } // (close file) bool close () { return ::close (fd) >= 0; } // (destructor) ~ File_Output () { free_name (filename); } }; // File_Output // // File input implemented // struct File_Input : A_Input { char const * filename; int fd; // (constructor) File_Input (int _fd) { fd = _fd; filename = 0; } // File_Input // (constructor) File_Input (char const *name) { fd = -1; filename = file_name (name); } // File_Input // (constructor) File_Input (char const *name, char const *path) { fd = -1; filename = path_name (name, path); } // File_Input // (constructor) File_Input (char const *name, char const *dir, char const *path) { fd = -1; filename = path_dir_name (name, dir, path); } // File_Input // (constructor) File_Input (char const *name, PathDir &path_dir) { fd = -1; filename = path_dir_name (name, path_dir.dir, path_dir.path); } // File_Input // (open input file) bool open () { fd = ::open (filename, O_RDONLY | O_BINARY); return fd >= 0; } // (read data block: returns # of bytes read) int read (char *data, unsigned count) { return ::read (fd, data, count); } // (read unsigned value) bool get_value (unsigned &value) { return read ((char *) &value, sizeof (value)) == sizeof (value); } // (get file length) bool get_length (unsigned &file_length) { struct stat file_stat; if (fstat (fd, &file_stat) == 0) { file_length = file_stat.st_size; return true; } return false; } // get_length // (get file change time) bool get_chtime (time_t &file_chtime) { struct stat file_stat; if (fstat (fd, &file_stat) == 0) { file_chtime = file_stat.st_mtime; return true; } return false; } // get_chtime // (close file) bool close () { return ::close (fd) >= 0; } // (destructor) ~ File_Input () { free_name (filename); } }; // File_Input // // File input/output implemented // struct File_InOut : A_InOut { char const * filename; int fd; // (constructor) File_InOut (char const *name) { fd = -1; filename = file_name (name); } // File_InOut // (constructor) File_InOut (char const *name, char const *path) { fd = -1; filename = path_name (name, path); } // File_InOut // (constructor) File_InOut (char const *name, char const *dir, char const *path) { fd = -1; filename = path_dir_name (name, dir, path); } // File_InOut // (constructor) File_InOut (char const *name, PathDir &path_dir) { fd = -1; filename = path_dir_name (name, path_dir.dir, path_dir.path); } // File_InOut // (open in/out file) bool open (bool do_init) { fd = ::open (filename, O_RDWR | O_BINARY | (do_init ? O_CREAT|O_TRUNC : 0), 0777); return fd >= 0; } // (open in read mode:) bool open_read () { fd = ::open (filename, O_RDONLY | O_BINARY, 0777); return fd >= 0; } // (open in create mode:) bool open_create () { return open (true); } // (open in update mode:) bool open_update () { return open (false); } // (read data block: // returns # of bytes read) int read (char *data, unsigned count) { return ::read (fd, data, count); } // (write data block: // returns # of bytes written) int write (char const *data, unsigned count) { return ::write (fd, data, count); } // (seek to position in file) bool seek (size_t offset) { return ::lseek (fd, offset, 0) == offset; } // (seek to start) unsigned seek_start () { return ::lseek (fd, 0, 0); } // (seek to end) unsigned seek_end () { return ::lseek (fd, 0, 2); } // (tell position) bool tell (size_t &offset) { int result = ::lseek (fd, 0, 1); if (result >= 0) { offset = result; return true; } return false; } // (reset to beginning) bool reset () { return seek (0); } // (read unsigned value) bool get_value (unsigned &value) { return read ((char *) &value, sizeof (value)) == sizeof (value); } // (write unsigned value) bool put_value (unsigned value) { return write ((char const *) &value, sizeof (value)) == sizeof (value); } // (close file) bool close () { return ::close (fd) >= 0; } // (destructor) ~ File_InOut () { free_name (filename); } }; // File_InOut // // File system implemented // // Abstract: file system operations struct File_System : A_System { unsigned flags; // (reserved) File_System (unsigned _flags) { flags = _flags; } // (file attributes) // get file size bool file_size (char const *file_name, unsigned &size); // get file date/time // (time_flag ? modify time : access time) bool file_time (char const *file_name, time_t &time, bool time_flag = true); // change file date/time // (time_flag ? modify time : access time) bool file_change_time (char const *file_name, time_t change_time, bool time_flag = true); // check file presence: bool is_exist (char const *file_name); bool is_exist (char const *file_name, char const *file_path); bool is_exist (char const *file_name, PathDir &file_path); // delete file bool delete_file (char const *file_name); bool delete_file (char const *file_name, PathDir &file_path); // rename file bool rename_file (char const *from_name, char const *to_name); bool rename_file (char const *from_name, char const *to_name, PathDir &file_path); // (directory controls) // check directory presence: bool is_dir (char const *dir_name); bool is_dir (char const *dir_name, char const *dir_path); // create directory: bool create_dir (char const *dir_name); bool create_dir (char const *dir_name, char const *dir_path); // change directory: bool change_dir (char const *dir_name); bool change_dir (char const *dir_name, char const *dir_path); // remove directory: bool remove_dir (char const *dir_name); bool remove_dir (char const *dir_name, char const *dir_path); }; // File_System #ifndef CONTAINER_H // get file size bool File_System::file_size (char const *file_name, unsigned &size) { struct stat file_stat; if (stat (file_name, &file_stat) == 0) { size = file_stat.st_size; return true; } return false; } // File_System::file_size // get file date/time // (time_flag ? modify time : access time) bool File_System::file_time (char const *file_name, time_t &time, bool time_flag) { struct stat file_stat; if (stat (file_name, &file_stat) == 0) { time = time_flag ? file_stat.st_mtime : file_stat.st_atime; return true; } return false; } // File_System::file_time // change file date/time // (time_flag ? modify time : access time) bool File_System::file_change_time (char const *file_name, time_t change_time, bool time_flag) { struct stat file_stat; struct utimbuf file_utime; if (stat (file_name, &file_stat) == 0) { if (time_flag) { // (change modify time) file_utime.actime = file_stat.st_atime; file_utime.modtime = change_time; } else { // (change access time) file_utime.actime = change_time; file_utime.modtime = file_stat.st_mtime; } } return utime (file_name, &file_utime) == 0; } // File_System::file_change_time // // check (ordinary) file presence // bool File_System::is_exist (char const *file_name) { struct stat file_stat; if (stat (file_name, &file_stat) == 0) return S_ISREG (file_stat.st_mode); return false; } // File_System::is_exist bool File_System::is_exist (char const *file_name, char const *file_path) { char const * _filename = path_name (file_name, file_path); bool result = is_exist (_filename); free_name (_filename); return result; } // File_System::is_exist bool File_System::is_exist (char const *file_name, PathDir &file_path) { char const * _filename = path_dir_name (file_name, file_path.dir, file_path.path); bool result = is_exist (_filename); free_name (_filename); return result; } // File_System::is_exist // // rename (ordinary) file // bool File_System::rename_file (char const *from_name, char const *to_name) { return ::rename (from_name, to_name) == 0; } // File_System::rename_file bool File_System::rename_file (char const *from_name, char const *to_name, PathDir &file_path) { char const * _file_from = path_dir_name (from_name, file_path.dir, file_path.path); char const * _file_to = path_dir_name (to_name, file_path.dir, file_path.path); bool result = rename_file (_file_from, _file_to); free_name (_file_from); free_name (_file_to); return result; } // File_System::rename_file // // delete (ordinary) file // bool File_System::delete_file (char const *file_name) { return ::unlink (file_name) == 0; } // File_System::delete_file bool File_System::delete_file (char const *file_name, PathDir &file_path) { char const * _filename = path_dir_name (file_name, file_path.dir, file_path.path); bool result = delete_file (_filename); free_name (_filename); return result; } // File_System::delete_file // // check directory presence // bool File_System::is_dir (char const *dir_name) { struct stat dir_stat; if (stat (dir_name, &dir_stat) == 0) return S_ISDIR (dir_stat.st_mode); return false; } // File_System::is_dir bool File_System::is_dir (char const *dir_name, char const *dir_path) { char const * _directory = path_name (dir_name, dir_path); bool result = is_dir (_directory); free_name (_directory); return result; } // File_System::is_dir // // create directory // bool File_System::create_dir (char const *dir_name) { return ::mkdir (dir_name) >= 0; } // File_System::create_dir bool File_System::create_dir (char const *dir_name, char const *dir_path) { char const * _directory = path_name (dir_name, dir_path); bool result = create_dir (_directory); free_name (_directory); return result; } // File_System::create_dir // // change current directory // bool File_System::change_dir (char const *dir_name) { return ::chdir (dir_name) >= 0; } // File_System::change_dir bool File_System::change_dir (char const *dir_name, char const *dir_path) { char const * _directory = path_name (dir_name, dir_path); bool result = change_dir (_directory); free_name (_directory); return result; } // File_System::change_dir // // remove directory // bool File_System::remove_dir (char const *dir_name) { return ::rmdir (dir_name) >= 0; } // File_System::remove_dir bool File_System::remove_dir (char const *dir_name, char const *dir_path) { char const * _directory = path_name (dir_name, dir_path); bool result = remove_dir (_directory); free_name (_directory); return result; } // File_System::remove_dir #endif #ifndef CONTAINER_H // // Read data block, [count] from {self} -> to 'output'. // Returns true on success. // bool A_InOut::read_to (A_Output &output, unsigned count) { enum { WriteLen = 512 }; char buffer [WriteLen]; int amount; while (amount = count) { if (amount > WriteLen) amount = WriteLen; if ((amount = read (buffer, amount)) < 0) return false; // ({self} read fail) if (amount) { if ((output.write (buffer, amount)) != amount) return false; // (output write fail) count -= amount; } } // while (count) return true; // (success) } // A_InOut::read_to // // Write data block, [count] to {self} <- from 'input'. // Returns true on success. // bool A_InOut::write_from (A_Input &input, unsigned count) { enum { ReadLen = 512 }; char buffer [ReadLen]; int amount; while (amount = count) { if (amount > ReadLen) amount = ReadLen; if ((amount = input.read (buffer, amount)) < 0) return false; // (input read fail) if (amount) { if (write (buffer, amount) != amount) return false; // ({self} write fail) count -= amount; } } // while (count) return true; // (success) } // A_InOut::write_from #endif /* For FILE based file operations, use _fileno() and _chsize_s() to change the size of a file. int changesize(FILE *fp, __int64 size) { int filedes = _fileno(fp); return _chsize_s(filedes, size); } A truncate version can be written by validating that the supplied size is less than the current file size, as _chsize_s() will truncate or extend a file's size - see http://msdn.microsoft.com/en-us/library/whx354w1(VS.80).aspx. */ // // Source lines iterator // struct LinesIterator { A_Input &source; char *buffer; unsigned buffer_size; // (constructor) LinesIterator (A_Input &_source, unsigned capacity) : source (_source) { buffer = new ("LinesIterator_buffer") char [capacity + 1]; buffer_size = capacity; } // (destructor) ~ LinesIterator () { delete [] buffer; } virtual void line_ready (char *line_pointer, unsigned line_no); // Engage: parse input void engage (); }; // LinesIterator #ifndef CONTAINER_H // Engage: parse input (calling 'line_ready') void LinesIterator::engage () { int count; unsigned lc = 0; unsigned offset = 0; buffer [buffer_size] = '\0'; while ((count = source.read (buffer + offset, buffer_size - offset)) > 0) { char *start = buffer, *end; buffer [offset + count] = '\0'; while (end = strchr (start, '\n')) { *end = '\0'; if (end [-1] == '\r') end [-1] = '\0'; line_ready (start, lc ++); start = end + 1; } if (offset = strlen (start)) { if (buffer != start) memcpy (buffer, start, offset); else { line_ready (start, lc ++); offset = 0; } } } // while (read) // (finally, terminate:) line_ready (0, lc); } // LinesIterator::engage void LinesIterator::line_ready (char *line_pointer, unsigned line_no) {} #endif