|
|
| version 1.52, 2002/02/08 07:27:42 | version 1.88, 2024/09/07 15:01:38 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: Array & Array_iter classes decls. | Parser: Array & Array_iterator classes decls. |
| Copyright (c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com) | Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com) |
| Author: Alexander Petrosyan <paf@design.ru> (http://paf.design.ru) | Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> |
| $Id$ | |
| */ | */ |
| #ifndef PA_ARRAY_H | #ifndef PA_ARRAY_H |
| #define PA_ARRAY_H | #define PA_ARRAY_H |
| #include "pa_pool.h" | #define IDENT_PA_ARRAY_H "$Id$" |
| #include "pa_types.h" | |
| #include "pa_string.h" | // includes |
| class Array_iter; | #include "pa_memory.h" |
| #include "pa_exception.h" | |
| /** | |
| Pooled Array. | // forwards |
| Internal structure: | template<typename T> class Array_iterator; |
| @verbatim | |
| Array Chunk0 | // defines |
| ====== ======== | |
| head--------------->[ptr] | #define ARRAY_OPTION_LIMIT_ALL ((size_t)-1) |
| append_here-------->[ptr] | |
| link_row ........ | /// Simple Array |
| . . | template<typename T> class Array: public PA_Object { |
| . [ptr] | |
| ...........>[link to the next chunk] | friend class Array_iterator<T>; |
| @endverbatim | |
| */ | protected: |
| /// elements[growing size] here | |
| T *felements; | |
| // allocated size | |
| size_t fallocated; | |
| // array size | |
| size_t fused; | |
| class Array : public Pooled { | |
| friend class Array_iter; | |
| public: | public: |
| typedef Array_iterator<T> Iterator; | |
| /// Array item type | struct Action_options { |
| typedef void Item; | size_t offset; |
| size_t limit; //< ARRAY_OPTION_LIMIT_ALL means 'all'. zero limit means 'nothing' | |
| bool reverse; | |
| bool defined; | |
| Action_options( | |
| size_t aoffset=0, | |
| size_t alimit=ARRAY_OPTION_LIMIT_ALL, | |
| bool areverse=false): | |
| offset(aoffset), limit(alimit), reverse(areverse), | |
| defined(false) {} | |
| bool adjust(size_t count) { | |
| if(!count || !limit) | |
| return false; | |
| if(offset>=count) | |
| return false; | |
| // max(limit) | |
| size_t m=reverse? | |
| offset+1 | |
| :count-offset; | |
| if(!m) | |
| return false; | |
| // fix limit | |
| if(limit==ARRAY_OPTION_LIMIT_ALL || limit>m) | |
| limit=m; | |
| /*/// for_each iterator function type, const info | return true; |
| typedef void (*For_each_func_const)(Item *value, const void *info); | } |
| */ | |
| /// for_each iterator function type | |
| typedef void (*For_each_func)(Item *value, void *info); | |
| /* | |
| /// for_each iterator function type, passing item storage address | |
| typedef void (*For_each_func_storage)(Item ** value, void *info); | |
| */ | |
| /// first_that iterator function type, const info | |
| typedef void *(*Item_that_func_const)(Item *value, const void *info); | |
| /// first_that iterator function type | |
| typedef void *(*Item_that_func)(Item *value, void *info); | |
| enum { | |
| CR_INITIAL_ROWS_DEFAULT=3, ///< default preallocated row count | |
| CR_GROW_COUNT=3 ///< each time the Array chunk_is_full() array expanded() | |
| }; | }; |
| public: | typedef T element_type; |
| inline Array(size_t initial=0): | |
| fallocated(initial), | |
| fused(0) | |
| { | |
| felements=fallocated?(T *)pa_malloc(fallocated*sizeof(T)):0; | |
| } | |
| Array(Pool& apool, int initial_rows=CR_INITIAL_ROWS_DEFAULT); | #ifdef USE_DESTRUCTORS |
| Array(const Array& source, int offset=0); | inline ~Array(){ |
| if(felements) | |
| pa_free(felements); | |
| } | |
| #endif | |
| /// size Array. how many items are in it | /// how many items are in Array |
| int size() const { return fused_rows; } | inline size_t count() const { return fused; } |
| /// append Item to array | /// append to array |
| Array& operator += (Item *src); | inline Array& operator+=(T src) { |
| /// append int value to array | if(is_full()) |
| Array& operator += (int value) { return *this+=reinterpret_cast<Item *>(value); } | expand(fallocated>0? 2+fallocated/32 : 3); // 3 is PAF default, confirmed by tests |
| /// dirty hack to allow constant items storage. I long for Array<const Item*> | felements[fused++]=src; |
| Array& operator += (const Item *src) { return *this+=const_cast<Item *>(src); } | |
| return *this; | |
| } | |
| /// append other Array portion to this one. starting from offset | /// append other Array portion to this one. starting from offset |
| Array& append_array(const Array& src, int offset=0); | Array& append(const Array& src, |
| size_t offset=0, | |
| size_t limit=ARRAY_OPTION_LIMIT_ALL) { //< negative limit means 'all'. zero limit means 'nothing' | |
| size_t src_count=src.count(); | |
| // skip tivials | |
| if(!src_count || !limit || offset>=src_count) | |
| return *this; | |
| // max(limit) | |
| size_t m=src_count-offset; | |
| // fix limit | |
| if(limit==ARRAY_OPTION_LIMIT_ALL || limit>m) | |
| limit=m; | |
| ssize_t delta=limit-(fallocated-fused); | |
| if(delta>0) | |
| expand(delta); | |
| T* from=&src.felements[offset]; | |
| T* to=&felements[fused]; | |
| for(T* from_end=from+limit; from<from_end; from++) | |
| *to++=*from; | |
| fused+=limit; | |
| return *this; | |
| } | |
| /// get index-element | |
| inline T get(size_t index) const { | |
| assert(index<count()); | |
| return felements[index]; | |
| } | |
| /// ref version of get | |
| inline T& get_ref(size_t index) const { | |
| assert(index<count()); | |
| return felements[index]; | |
| } | |
| /// put index-element | |
| inline void put(size_t index, T element) { | |
| assert(index<count()); | |
| felements[index]=element; | |
| } | |
| /// insert index-element | |
| inline void insert(size_t index, T element) { | |
| assert(index<=count()); | |
| if(is_full()) | |
| expand(fallocated>0? 2+fallocated/32 : 3); // 3 is PAF default, confirmed by tests | |
| memmove(felements+index+1, felements+index, (fused-index) * sizeof(T)); | |
| Item *get(int index) const; | felements[index]=element; |
| int get_int(int index) const { return reinterpret_cast<int>(get(index)); } | fused++; |
| } | |
| void put(int index, Item *item); | /// remove index-element |
| void put_int(int index, int value) { put(index, reinterpret_cast<Item *>(value)); } | inline void remove(size_t index) { |
| /// convinient way to get strings from Array. I long for Array<const String *> | assert(index<count()); |
| const String *get_string(int index) const { | if (index<--fused) |
| return const_cast<const String *>(static_cast<String *>(get(index))); | memmove(felements+index, felements+index+1, (fused-index) * sizeof(T)); |
| } | } |
| /*/// iterate over all elements, const info | inline T operator [](size_t index) const { return get(index); } |
| void for_each(For_each_func_const func, const void *info=0) const; | |
| */ | inline void clear() { |
| fused=0; | |
| } | |
| /// iterate over all elements | /// iterate over all elements |
| void for_each(For_each_func func, void *info=0) const; | template<typename I> void for_each(void (*callback)(T, I), I info) const { |
| T *last=felements+fused; | |
| for(T *current=felements; current<last; current++) | |
| callback(*current, info); | |
| } | |
| /*/// iterate over all elements, passing address of item storage | /// iterate over all elements |
| void for_each(For_each_func_storage func, void *info=0); | template<typename I> void for_each(bool (*callback)(T, I), I info) const { |
| */ | T *last=felements+fused; |
| for(T *current=felements; current<last; current++) | |
| /// iterate over all elements until condition, const info | if(callback(*current, info)) |
| void* first_that(Item_that_func_const func, const void *info=0) const; | return; |
| } | |
| /// iterate over all elements until condition | |
| void* first_that(Item_that_func func, void *info=0) const; | |
| private: | |
| /// constructor helper | |
| void construct_new(int initial_rows); | |
| private: | |
| /// several record elements | |
| struct Chunk { | |
| int count; ///< the number of rows in chunk | |
| /// item or a link to next chunk union | |
| union Row { | |
| Item *item; | |
| Chunk *link; ///< link to the next chunk in chain | |
| } rows[1]; | |
| // next rows are here | |
| } | |
| *head; ///< the head chunk of the chunk chain | |
| /** last allocated chunk | |
| helps appending Arrays | |
| */ | |
| Chunk *tail; | |
| /// next append would write to this record | |
| Chunk::Row *append_here; | |
| /** the address of place where lies address | |
| of the link to the next chunk to allocate | |
| */ | |
| Chunk::Row *link_row; | |
| private: | /// iterate over all elements |
| template<typename I> void for_each_ref(void (*callback)(T&, I), I info) { | |
| T *last=felements+fused; | |
| for(T *current=felements; current<last; current++) | |
| callback(*current, info); | |
| } | |
| // array size | /// iterate over all elements until condition becomes true, return that element |
| int fused_rows; | template<typename I> T first_that(bool (*callback)(T, I), I info) const { |
| T *last=felements+fused; | |
| for(T *current=felements; current<last; current++) | |
| if(callback(*current, info)) | |
| return *current; | |
| return T(0); | |
| } | |
| private: | inline T* ptr(size_t index){ |
| return felements + index; | |
| } | |
| void fit(size_t index, T element){ | |
| if(index >= fallocated){ | |
| size_t new_allocated=fallocated>0 ? fallocated : 3; | |
| while(new_allocated <= index){ | |
| new_allocated+=2 + new_allocated/32; | |
| } | |
| expand(new_allocated - fallocated); | |
| } | |
| felements[index]=element; | |
| if(index >= fused){ | |
| fused=index+1; | |
| } | |
| } | |
| protected: | |
| bool chunk_is_full() { | bool is_full() { |
| return append_here == link_row; | return fused == fallocated; |
| } | |
| void expand(size_t delta) { | |
| if(fallocated){ | |
| size_t new_allocated=fallocated+delta; | |
| felements=(T *)pa_realloc(felements, new_allocated*sizeof(T)); | |
| fallocated=new_allocated; | |
| } else { | |
| fallocated=delta; | |
| felements=(T *)pa_malloc(fallocated*sizeof(T)); | |
| } | |
| } | } |
| void expand(int chunk_rows); | |
| private: //disabled | private: //disabled |
| //Array(Array&) { } | Array(const Array&) {} |
| Array& operator = (const Array&) { return *this; } | Array& operator = (const Array&) { return *this; } |
| }; | }; |
| /// handy array iterator | /** Array iterator, usage: |
| class Array_iter { | @code |
| // Array<T> a; | |
| for(Array_iterator<T> i(a); i.has_next(); ) { | |
| T& element=i.next(); | |
| ... | |
| } | |
| @endcode | |
| */ | |
| template<typename T> class Array_iterator { | |
| const Array<T>& farray; | |
| T *fcurrent; | |
| T *flast; | |
| public: | public: |
| Array_iter(const Array& aarray) : array(aarray), | Array_iterator(const Array<T>& aarray): farray(aarray) { |
| chunk(aarray.head), | fcurrent=farray.felements; |
| row(chunk->rows), | flast=farray.felements+farray.count(); |
| countdown(chunk->count) { | |
| } | } |
| /// there are still elements | /// there are still elements |
| bool has_next() { | bool has_next() { |
| return !(chunk==array.tail && row==array.append_here); | return fcurrent<flast; |
| } | } |
| /// quickly extracts next Array::Item | /// returns the current element and advances the iterator |
| Array::Item *next() { | T next() { |
| // assuming: never called after has_next()! | return *(fcurrent++); |
| if(countdown==0) { // end of chunk? | |
| chunk=row->link; | |
| row=chunk->rows; | |
| countdown=chunk->count; | |
| } | |
| Array::Item *result=row->item; | |
| row++; countdown--; | |
| return result; | |
| } | } |
| /// quickly extracts next Array::Item as const String | /// returns the current element |
| const String *next_string() { | T value() { |
| return const_cast<const String *>(static_cast<String *>(next())); | return *(fcurrent); |
| } | } |
| private: | // returns the current index of the iterator |
| const Array& array; | size_t index() { |
| const Array::Chunk *chunk; | return fcurrent - farray.felements; |
| const Array::Chunk::Row *row; | } |
| int countdown; | |
| }; | }; |
| #endif | #endif |