1 /*******************************************************
2 
3    CoolReader Engine DOM Tree
4 
5    LDOMNodeIdMap.cpp:  Name to Id map
6 
7    (c) Vadim Lopatin, 2000-2006
8    This source code is distributed under the terms of
9    GNU General Public License
10    See LICENSE file for details
11 
12 *******************************************************/
13 
14 #include "../include/lvmemman.h"
15 #include "../include/lstridmap.h"
16 #include "../include/dtddef.h"
17 #include "../include/lvtinydom.h"
18 #include <string.h>
19 
LDOMNameIdMapItem(lUInt16 _id,const lString32 & _value,const css_elem_def_props_t * _data)20 LDOMNameIdMapItem::LDOMNameIdMapItem(lUInt16 _id, const lString32 & _value, const css_elem_def_props_t * _data)
21     : id(_id), value(_value)
22 {
23 	if ( _data ) {
24         data = new css_elem_def_props_t();
25 		*data = *_data;
26 	} else
27 		data = NULL;
28 }
29 
LDOMNameIdMapItem(LDOMNameIdMapItem & item)30 LDOMNameIdMapItem::LDOMNameIdMapItem(LDOMNameIdMapItem & item)
31     : id(item.id), value(item.value)
32 {
33 	if ( item.data ) {
34 		data = new css_elem_def_props_t();
35 		*data = *item.data;
36 	} else {
37 		data = NULL;
38 	}
39 }
40 
41 
42 static const char id_map_item_magic[] = "IDMI";
43 
44 /// serialize to byte array
serialize(SerialBuf & buf)45 void LDOMNameIdMapItem::serialize( SerialBuf & buf )
46 {
47     if ( buf.error() )
48         return;
49 	buf.putMagic( id_map_item_magic );
50 	buf << id;
51 	buf << value;
52 	if ( data ) {
53 		buf << (lUInt8)1;
54 		buf << (lUInt8)data->display;
55 		buf << (lUInt8)data->white_space;
56 		buf << data->allow_text;
57 		buf << data->is_object;
58 	} else {
59 		buf << (lUInt8)0;
60 	}
61 }
62 
63 /// deserialize from byte array
deserialize(SerialBuf & buf)64 LDOMNameIdMapItem * LDOMNameIdMapItem::deserialize( SerialBuf & buf )
65 {
66     if ( buf.error() )
67         return NULL;
68 	if ( !buf.checkMagic( id_map_item_magic ) )
69         return NULL;
70 	lUInt16 id;
71 	lString32 value;
72 	lUInt8 flgData;
73     buf >> id >> value >> flgData;
74     if ( id>=MAX_TYPE_ID )
75         return NULL;
76     if ( flgData ) {
77         css_elem_def_props_t props;
78         lUInt8 display;
79         lUInt8 white_space;
80         buf >> display >> white_space >> props.allow_text >> props.is_object;
81         if ( display > css_d_none || white_space > css_ws_break_spaces )
82             return NULL;
83         props.display = (css_display_t)display;
84         props.white_space = (css_white_space_t)white_space;
85     	return new LDOMNameIdMapItem(id, value, &props);
86     }
87    	return new LDOMNameIdMapItem(id, value, NULL);
88 }
89 
~LDOMNameIdMapItem()90 LDOMNameIdMapItem::~LDOMNameIdMapItem()
91 {
92 	if ( data )
93 		delete data;
94 }
95 
96 static const char id_map_magic[] = "IMAP";
97 
98 /// serialize to byte array (pointer will be incremented by number of bytes written)
serialize(SerialBuf & buf)99 void LDOMNameIdMap::serialize( SerialBuf & buf )
100 {
101     if ( buf.error() )
102         return;
103     if (!m_sorted)
104         Sort();
105     int start = buf.pos();
106 	buf.putMagic( id_map_magic );
107     buf << m_count;
108     for ( int i=0; i<m_size; i++ ) {
109         if ( m_by_id[i] )
110             m_by_id[i]->serialize( buf );
111     }
112     buf.putCRC( buf.pos() - start );
113     m_changed = false;
114 }
115 
116 /// deserialize from byte array (pointer will be incremented by number of bytes read)
deserialize(SerialBuf & buf)117 bool LDOMNameIdMap::deserialize( SerialBuf & buf )
118 {
119     if ( buf.error() )
120         return false;
121     int start = buf.pos();
122     if ( !buf.checkMagic( id_map_magic ) ) {
123         buf.seterror();
124         return false;
125     }
126     Clear();
127     lUInt16 count;
128     buf >> count;
129     if ( count>m_size ) {
130         buf.seterror();
131         return false;
132     }
133     for ( int i=0; i<count; i++ ) {
134         LDOMNameIdMapItem * item = LDOMNameIdMapItem::deserialize(buf);
135         if ( !item || (item->id<m_size && m_by_id[item->id]!=NULL ) ) { // invalid entry
136             if ( item )
137                 delete item;
138             buf.seterror();
139             return false;
140         }
141         AddItem( item );
142     }
143     m_sorted = false;
144     buf.checkCRC( buf.pos() - start );
145     m_changed = false;
146     if (!m_sorted)
147         Sort();
148     return !buf.error();
149 }
150 
151 
LDOMNameIdMap(lUInt16 maxId)152 LDOMNameIdMap::LDOMNameIdMap(lUInt16 maxId)
153 {
154     m_size = maxId+1;
155     m_count = 0;
156     m_by_id   = new LDOMNameIdMapItem * [m_size]();
157     m_by_name = new LDOMNameIdMapItem * [m_size]();
158     m_sorted = true;
159     m_changed = false;
160 }
161 
162 /// Copy constructor
LDOMNameIdMap(LDOMNameIdMap & map)163 LDOMNameIdMap::LDOMNameIdMap( LDOMNameIdMap & map )
164 {
165     m_changed = false;
166     m_size = map.m_size;
167     m_count = map.m_count;
168     m_by_id   = new LDOMNameIdMapItem * [m_size];
169     int i;
170     for ( i=0; i<m_size; i++ ) {
171         if ( map.m_by_id[i] )
172             m_by_id[i] = new LDOMNameIdMapItem( *map.m_by_id[i] );
173         else
174             m_by_id[i] = NULL;
175     }
176     m_by_name = new LDOMNameIdMapItem * [m_size];
177     for ( i=0; i<m_size; i++ ) {
178         if ( map.m_by_name[i] )
179             m_by_name[i] = new LDOMNameIdMapItem( *map.m_by_name[i] );
180         else
181             m_by_name[i] = NULL;
182     }
183     m_sorted = map.m_sorted;
184 }
185 
~LDOMNameIdMap()186 LDOMNameIdMap::~LDOMNameIdMap()
187 {
188     Clear();
189     delete[] m_by_name;
190     delete[] m_by_id;
191 }
192 
compare_items(const void * item1,const void * item2)193 static int compare_items( const void * item1, const void * item2 )
194 {
195     return (*((LDOMNameIdMapItem **)item1))->value.compare( (*((LDOMNameIdMapItem **)item2))->value );
196 }
197 
Sort()198 void LDOMNameIdMap::Sort()
199 {
200     if (m_count>1)
201         qsort( m_by_name, m_count, sizeof(LDOMNameIdMapItem*), compare_items );
202     m_sorted = true;
203 }
204 
findItem(const lChar32 * name)205 const LDOMNameIdMapItem * LDOMNameIdMap::findItem( const lChar32 * name )
206 {
207     if (m_count==0 || !name || !*name)
208         return NULL;
209     if (!m_sorted)
210         Sort();
211     lUInt16 a, b, c;
212     int r;
213     a = 0;
214     b = m_count;
215     for (;;) {
216         c = (a + b)>>1;
217         r = lStr_cmp( name, m_by_name[c]->value.c_str() );
218         if (r == 0)
219             return m_by_name[c]; // found
220         if (b==a+1)
221             return NULL; // not found
222         if (r>0) {
223             a = c;
224         } else {
225             b = c;
226         }
227     }
228 }
229 
findItem(const lChar8 * name)230 const LDOMNameIdMapItem * LDOMNameIdMap::findItem( const lChar8 * name )
231 {
232     if (m_count==0 || !name || !*name)
233         return NULL;
234     if (!m_sorted)
235         Sort();
236     lUInt16 a, b, c;
237     int r;
238     a = 0;
239     b = m_count;
240     for (;;) {
241         c = (a + b)>>1;
242         r = lStr_cmp( name, m_by_name[c]->value.c_str() );
243         if (r == 0)
244             return m_by_name[c]; // found
245         if (b==a+1)
246             return NULL; // not found
247         if (r>0) {
248             a = c;
249         } else {
250             b = c;
251         }
252     }
253 }
254 
AddItem(LDOMNameIdMapItem * item)255 void LDOMNameIdMap::AddItem( LDOMNameIdMapItem * item )
256 {
257     if ( item==NULL )
258         return;
259     if ( item->id==0 ) {
260         delete item;
261         return;
262     }
263     if (item->id>=m_size)
264     {
265         // reallocate storage
266         lUInt16 newsize = item->id+16;
267         m_by_id = cr_realloc( m_by_id, newsize );
268         m_by_name = cr_realloc( m_by_name, newsize );
269         for (lUInt16 i = m_size; i<newsize; i++)
270         {
271             m_by_id[i] = NULL;
272             m_by_name[i] = NULL;
273         }
274         m_size = newsize;
275     }
276     if (m_by_id[item->id] != NULL)
277     {
278         delete item;
279         return; // already exists
280     }
281     m_by_id[item->id] = item;
282     m_by_name[m_count++] = item;
283     m_sorted = false;
284     if (!m_changed) {
285         m_changed = true;
286         //CRLog::trace("new ID for %s is %d", LCSTR(item->value), item->id);
287     }
288 }
289 
AddItem(lUInt16 id,const lString32 & value,const css_elem_def_props_t * data)290 void LDOMNameIdMap::AddItem( lUInt16 id, const lString32 & value, const css_elem_def_props_t * data )
291 {
292     if (id==0)
293         return;
294     LDOMNameIdMapItem * item = new LDOMNameIdMapItem( id, value, data );
295     AddItem( item );
296 }
297 
298 
Clear()299 void LDOMNameIdMap::Clear()
300 {
301     for (lUInt16 i = 0; i<m_count; i++)
302     {
303         if (m_by_name[i])
304             delete m_by_name[i];
305     }
306     memset( m_by_id, 0, sizeof(LDOMNameIdMapItem *)*m_size);
307     m_count = 0;
308 }
309 
dumpUnknownItems(FILE * f,int start_id)310 void LDOMNameIdMap::dumpUnknownItems( FILE * f, int start_id )
311 {
312     for (int i=start_id; i<m_size; i++)
313     {
314         if (m_by_id[i] != NULL)
315         {
316             lString8 s8( m_by_id[i]->value.c_str() );
317             fprintf( f, "%d %s\n", m_by_id[i]->id, s8.c_str() );
318         }
319     }
320 }
321 
getUnknownItems(int start_id)322 lString32 LDOMNameIdMap::getUnknownItems( int start_id )
323 {
324     lString32 items;
325     for (int i=start_id; i<m_size; i++) {
326         if (m_by_id[i] != NULL) {
327             if ( !items.empty() )
328                 items << " ";
329             items << m_by_id[i]->value;
330         }
331     }
332     return items;
333 }
334