|
|
| version 1.5, 2001/01/29 10:15:14 | version 1.105, 2025/08/01 16:14:44 |
|---|---|
| Line 1 | Line 1 |
| /* | /** @file |
| $Id$ | Parser: Array & Array_iterator classes decls. |
| */ | |
| /* | |
| Array Chunk0 | |
| ====== ======== | |
| head--------------->[ptr] | |
| append_here-------->[ptr] | |
| link_row ........ | |
| . . | |
| . [ptr] | |
| ...........>[link to the next chunk] | |
| Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) | |
| Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> | |
| */ | */ |
| #ifndef PA_ARRAY_H | #ifndef PA_ARRAY_H |
| #define PA_ARRAY_H | #define PA_ARRAY_H |
| #include <stddef.h> | #define IDENT_PA_ARRAY_H "$Id$" |
| // includes | |
| #include "pa_memory.h" | |
| #include "pa_types.h" | #include "pa_types.h" |
| #include "pa_exception.h" | |
| // forwards | |
| template<typename T> class Array_iterator; | |
| template<typename T> class Array_robust_iterator; | |
| template<typename T> class Array_reverse_iterator; | |
| // defines | |
| class Pool; | #define ARRAY_OPTION_LIMIT_ALL ((size_t)-1) |
| /// Simple Array | |
| template<typename T> class Array: public PA_Object { | |
| friend class Array_iterator<T>; | |
| friend class Array_robust_iterator<T>; | |
| friend class Array_reverse_iterator<T>; | |
| protected: | |
| /// elements[growing size] here | |
| T *felements; | |
| // allocated size | |
| size_t fallocated; | |
| // array size | |
| size_t fsize; | |
| class Array { | |
| public: | public: |
| typedef void *Item; | typedef Array_iterator<T> Iterator; |
| typedef Array_robust_iterator<T> RobustIterator; | |
| typedef Array_reverse_iterator<T> ReverseIterator; | |
| struct Action_options { | |
| 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>m) | |
| limit=m; | |
| enum { | return true; |
| CR_PREALLOCATED_COUNT=10, | } |
| CR_GROW_PERCENT=60 | |
| }; | }; |
| private: | typedef T element_type; |
| friend Pool; | |
| inline Array(size_t initial=0): | |
| fallocated(initial), | |
| fsize(0) | |
| { | |
| felements=fallocated?(T *)pa_malloc(fallocated*sizeof(T)):0; | |
| } | |
| // the pool I'm allocated on | #ifdef USE_DESTRUCTORS |
| Pool *pool; | inline ~Array(){ |
| if(felements) | |
| pa_free(felements); | |
| } | |
| #endif | |
| struct Chunk { | /// how many items are in Array |
| // the number of rows in chunk | inline size_t count() const { return fsize; } |
| int count; | |
| 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: | /// append to array |
| // array size | inline Array& operator+=(T src) { |
| int fused_rows; | if(is_full()) |
| expand(); | |
| int cache_chunk_base; | felements[fsize++]=src; |
| Chunk *cache_chunk; | |
| private: | |
| // new&constructors made private to enforce factory manufacturing at pool | |
| void *operator new(size_t size, Pool *apool); | |
| void construct(Pool *apool, int initial_rows); | return *this; |
| Array(Pool *apool) { | |
| construct(apool, CR_PREALLOCATED_COUNT); | |
| } | } |
| Array(Pool *apool, int initial_rows) { | |
| construct(apool, initial_rows); | /// append other Array portion to this one. starting from offset |
| void append(const Array& src, size_t offset=0, size_t limit=ARRAY_OPTION_LIMIT_ALL) { //< zero limit means 'nothing' | |
| size_t src_count=src.count(); | |
| // skip tivials | |
| if(!src_count || !limit || offset>=src_count) | |
| return; | |
| // max(limit) | |
| size_t m=src_count-offset; | |
| // fix limit | |
| if(limit>m) | |
| limit=m; | |
| fit(fsize-1+limit); | |
| memcpy(felements+fsize, src.felements+offset, limit * sizeof(T)); | |
| fsize+=limit; | |
| } | } |
| /// get index-element | |
| inline T get(size_t index) const { | |
| assert(index<count()); | |
| return felements[index]; | |
| } | |
| bool chunk_is_full() { | /// ref version of get |
| return append_here == link_row; | inline T& get_ref(size_t index) const { |
| assert(index<count()); | |
| return felements[index]; | |
| } | } |
| void expand(int chunk_rows); | |
| public: | /// 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(); | |
| memmove(felements+index+1, felements+index, (fsize-index) * sizeof(T)); | |
| felements[index]=element; | |
| fsize++; | |
| } | |
| /// remove index-element | |
| inline void remove(size_t index) { | |
| assert(index<count()); | |
| if (index<--fsize) | |
| memmove(felements+index, felements+index+1, (fsize-index) * sizeof(T)); | |
| } | |
| inline T operator [](size_t index) const { return get(index); } | |
| inline void clear() { | |
| if(fsize) | |
| memset((void *)felements, 0, fsize * sizeof(T)); | |
| fsize=0; | |
| } | |
| /// iterate over all elements | |
| template<typename I> void for_each(void (*callback)(T, I), I info) const { | |
| T *last=felements+fsize; | |
| for(T *current=felements; current<last; current++) | |
| callback(*current, info); | |
| } | |
| /// iterate over all elements | |
| template<typename I> void for_each(bool (*callback)(T, I), I info) const { | |
| T *last=felements+fsize; | |
| for(T *current=felements; current<last; current++) | |
| if(callback(*current, info)) | |
| return; | |
| } | |
| /// iterate over all elements | |
| template<typename I> void for_each_ref(void (*callback)(T&, I), I info) { | |
| T *last=felements+fsize; | |
| for(T *current=felements; current<last; current++) | |
| callback(*current, info); | |
| } | |
| /// iterate over all elements until condition becomes true, return that element | |
| template<typename I> T first_that(bool (*callback)(T, I), I info) const { | |
| T *last=felements+fsize; | |
| for(T *current=felements; current<last; current++) | |
| if(callback(*current, info)) | |
| return *current; | |
| return T(0); | |
| } | |
| inline T* ptr(size_t index){ | |
| return felements + index; | |
| } | |
| protected: | |
| inline bool is_full() { | |
| return fsize == fallocated; | |
| } | |
| inline void expand() { | |
| resize(fallocated>0 ? fallocated+fallocated/2+2 : 3); // 3 is PAF default, confirmed by tests | |
| } | |
| int size() { return fused_rows; } | inline void fit(size_t index){ |
| Array& operator += (Item src); | if(index >= fallocated) |
| Item& operator [] (int index); | resize(max(fallocated+fallocated/4, index+1)); |
| Array& operator += (Array& src); | } |
| void resize(size_t asize) { | |
| if(fallocated){ | |
| felements=(T *)pa_realloc(felements, asize*sizeof(T)); | |
| #ifdef PA_DEBUG_DISABLE_GC | |
| // non-gc realloc doesn't zero; manually zero expanded region | |
| if(asize > fallocated) | |
| memset((void *)(felements+fallocated), 0, (asize-fallocated) * sizeof(T)); | |
| #endif | |
| fallocated=asize; | |
| } else { | |
| fallocated=asize; | |
| felements=(T *)pa_malloc(asize*sizeof(T)); | |
| } | |
| } | |
| private: //disabled | private: //disabled |
| Array& operator = (Array& src) { return *this; } | Array(const Array&) {} |
| Array(Array& src) {} | Array& operator = (const Array&) { return *this; } |
| }; | }; |
| /// Commonly used, templated to work with any integer type | |
| template<typename T> char* pa_itoa(T n, T base=10){ | |
| char buf[MAX_NUMBER + 1]; | |
| char* pos=buf + MAX_NUMBER; | |
| *pos='\0'; | |
| bool negative=n < 0; | |
| if (n < 0){ | |
| n=-n; | |
| } | |
| do { | |
| *(--pos)=(char)(n % base) + '0'; | |
| n/=base; | |
| } while (n > 0); | |
| if (negative) { | |
| *(--pos) = '-'; | |
| } | |
| return pa_strdup(pos, buf + MAX_NUMBER - pos); | |
| } | |
| template<typename T> char* pa_uitoa(T n, T base=10){ | |
| char buf[MAX_NUMBER + 1]; | |
| char* pos=buf + MAX_NUMBER; | |
| *pos='\0'; | |
| do { | |
| *(--pos)=(char)(n % base) + '0'; | |
| n/=base; | |
| } while (n > 0); | |
| return pa_strdup(pos, buf + MAX_NUMBER - pos); | |
| } | |
| /** Array iterator, usage: | |
| @code | |
| // Array<T> a; | |
| for(Array_iterator<T> i(a); i; ) { | |
| T& element=i.next(); | |
| ... | |
| } | |
| @endcode | |
| */ | |
| template<typename T> class Array_iterator { | |
| const Array<T>& farray; | |
| T *fcurrent; | |
| T *flast; | |
| public: | |
| Array_iterator(const Array<T>& aarray): farray(aarray) { | |
| fcurrent=farray.felements; | |
| flast=farray.felements + farray.fsize; | |
| } | |
| /// there are still elements | |
| inline operator bool () { | |
| return fcurrent < flast; | |
| } | |
| /// returns the current element and advances the iterator | |
| inline T next() { | |
| return *(fcurrent++); | |
| } | |
| /// returns the current element | |
| inline T value() { | |
| return *(fcurrent); | |
| } | |
| // returns the current index of the iterator | |
| inline size_t index() { | |
| return fcurrent - farray.felements; | |
| } | |
| }; | |
| // Slower array iterator for arrays that can be modified during iteration | |
| template<typename T> class Array_robust_iterator { | |
| const Array<T>& farray; | |
| size_t findex; | |
| public: | |
| Array_robust_iterator(const Array<T>& aarray) : farray(aarray), findex(0) {} | |
| inline operator bool() { | |
| return findex < farray.fsize; | |
| } | |
| inline void next() { | |
| findex++; | |
| } | |
| inline T value() { | |
| return farray.felements[findex]; | |
| } | |
| inline size_t index() { | |
| return findex; | |
| } | |
| }; | |
| // Robust as used for arrays that can be modified during iteration | |
| template<typename T> class Array_reverse_iterator { | |
| const Array<T>& farray; | |
| size_t findex; | |
| public: | |
| Array_reverse_iterator(const Array<T>& aarray): farray(aarray), findex(aarray.fsize) {} | |
| inline operator bool () { | |
| return (findex > 0) && (findex <= farray.fsize); | |
| } | |
| inline T prev() { | |
| return farray.felements[--findex]; | |
| } | |
| inline size_t index() { | |
| return findex; | |
| } | |
| }; | |
| #endif | #endif |