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