Coverage Report

Created: 2024-08-16 12:09

/src/libxml2/dict.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * dict.c: dictionary of reusable strings, just used to avoid allocation
3
 *         and freeing operations.
4
 *
5
 * Copyright (C) 2003-2012 Daniel Veillard.
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
12
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
14
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
15
 *
16
 * Author: daniel@veillard.com
17
 */
18
19
#define IN_LIBXML
20
#include "libxml.h"
21
22
#include <limits.h>
23
#include <stdlib.h>
24
#include <time.h>
25
26
#include "private/dict.h"
27
28
/*
29
 * Following http://www.ocert.org/advisories/ocert-2011-003.html
30
 * it seems that having hash randomization might be a good idea
31
 * when using XML with untrusted data
32
 * Note1: that it works correctly only if compiled with WITH_BIG_KEY
33
 *  which is the default.
34
 * Note2: the fast function used for a small dict won't protect very
35
 *  well but since the attack is based on growing a very big hash
36
 *  list we will use the BigKey algo as soon as the hash size grows
37
 *  over MIN_DICT_SIZE so this actually works
38
 */
39
#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
40
#define DICT_RANDOMIZATION
41
#endif
42
43
#include <string.h>
44
#ifdef HAVE_STDINT_H
45
#include <stdint.h>
46
#else
47
#ifdef HAVE_INTTYPES_H
48
#include <inttypes.h>
49
#elif defined(_WIN32)
50
typedef unsigned __int32 uint32_t;
51
#endif
52
#endif
53
#include <libxml/tree.h>
54
#include <libxml/dict.h>
55
#include <libxml/xmlmemory.h>
56
#include <libxml/xmlerror.h>
57
#include <libxml/globals.h>
58
59
/* #define DEBUG_GROW */
60
/* #define DICT_DEBUG_PATTERNS */
61
62
10.6M
#define MAX_HASH_LEN 3
63
44.3M
#define MIN_DICT_SIZE 128
64
12.9k
#define MAX_DICT_HASH 8 * 2048
65
#define WITH_BIG_KEY
66
67
#ifdef WITH_BIG_KEY
68
#define xmlDictComputeKey(dict, name, len)                              \
69
38.7M
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
70
38.7M
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
71
38.7M
     xmlDictComputeBigKey(name, len, (dict)->seed))
72
73
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
74
117k
    (((prefix) == NULL) ?                                               \
75
117k
      (xmlDictComputeKey(dict, name, len)) :                             \
76
117k
      (((dict)->size == MIN_DICT_SIZE) ?                                \
77
117k
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
78
117k
       xmlDictComputeBigQKey(prefix, plen, name, len, (dict)->seed)))
79
80
#else /* !WITH_BIG_KEY */
81
#define xmlDictComputeKey(dict, name, len)                              \
82
        xmlDictComputeFastKey(name, len, (dict)->seed)
83
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
84
        xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed)
85
#endif /* WITH_BIG_KEY */
86
87
/*
88
 * An entry in the dictionary
89
 */
90
typedef struct _xmlDictEntry xmlDictEntry;
91
typedef xmlDictEntry *xmlDictEntryPtr;
92
struct _xmlDictEntry {
93
    struct _xmlDictEntry *next;
94
    const xmlChar *name;
95
    unsigned int len;
96
    int valid;
97
    unsigned long okey;
98
};
99
100
typedef struct _xmlDictStrings xmlDictStrings;
101
typedef xmlDictStrings *xmlDictStringsPtr;
102
struct _xmlDictStrings {
103
    xmlDictStringsPtr next;
104
    xmlChar *free;
105
    xmlChar *end;
106
    size_t size;
107
    size_t nbStrings;
108
    xmlChar array[1];
109
};
110
/*
111
 * The entire dictionary
112
 */
113
struct _xmlDict {
114
    int ref_counter;
115
116
    struct _xmlDictEntry *dict;
117
    size_t size;
118
    unsigned int nbElems;
119
    xmlDictStringsPtr strings;
120
121
    struct _xmlDict *subdict;
122
    /* used for randomization */
123
    int seed;
124
    /* used to impose a limit on size */
125
    size_t limit;
126
};
127
128
/*
129
 * A mutex for modifying the reference counter for shared
130
 * dictionaries.
131
 */
132
static xmlMutexPtr xmlDictMutex = NULL;
133
134
/*
135
 * Whether the dictionary mutex was initialized.
136
 */
137
static int xmlDictInitialized = 0;
138
139
#ifdef DICT_RANDOMIZATION
140
#ifdef HAVE_RAND_R
141
/*
142
 * Internal data for random function, protected by xmlDictMutex
143
 */
144
static unsigned int rand_seed = 0;
145
#endif
146
#endif
147
148
/**
149
 * xmlInitializeDict:
150
 *
151
 * DEPRECATED: This function will be made private. Call xmlInitParser to
152
 * initialize the library.
153
 *
154
 * Do the dictionary mutex initialization.
155
 *
156
 * Returns 0 if initialization was already done, and 1 if that
157
 * call led to the initialization
158
 */
159
2.62k
int xmlInitializeDict(void) {
160
2.62k
    return(0);
161
2.62k
}
162
163
/**
164
 * __xmlInitializeDict:
165
 *
166
 * This function is not public
167
 * Do the dictionary mutex initialization.
168
 * this function is not thread safe, initialization should
169
 * normally be done once at setup when called from xmlOnceInit()
170
 * we may also land in this code if thread support is not compiled in
171
 *
172
 * Returns 0 if initialization was already done, and 1 if that
173
 * call led to the initialization
174
 */
175
2.62k
int __xmlInitializeDict(void) {
176
2.62k
    if (xmlDictInitialized)
177
0
        return(1);
178
179
2.62k
    if ((xmlDictMutex = xmlNewMutex()) == NULL)
180
0
        return(0);
181
2.62k
    xmlMutexLock(xmlDictMutex);
182
183
#ifdef DICT_RANDOMIZATION
184
#ifdef HAVE_RAND_R
185
    rand_seed = time(NULL);
186
    rand_r(& rand_seed);
187
#else
188
    srand(time(NULL));
189
#endif
190
#endif
191
2.62k
    xmlDictInitialized = 1;
192
2.62k
    xmlMutexUnlock(xmlDictMutex);
193
2.62k
    return(1);
194
2.62k
}
195
196
#ifdef DICT_RANDOMIZATION
197
int __xmlRandom(void) {
198
    int ret;
199
200
    if (xmlDictInitialized == 0)
201
        __xmlInitializeDict();
202
203
    xmlMutexLock(xmlDictMutex);
204
#ifdef HAVE_RAND_R
205
    ret = rand_r(& rand_seed);
206
#else
207
    ret = rand();
208
#endif
209
    xmlMutexUnlock(xmlDictMutex);
210
    return(ret);
211
}
212
#endif
213
214
/**
215
 * xmlDictCleanup:
216
 *
217
 * DEPRECATED: This function will be made private. Call xmlCleanupParser
218
 * to free global state but see the warnings there. xmlCleanupParser
219
 * should be only called once at program exit. In most cases, you don't
220
 * have call cleanup functions at all.
221
 *
222
 * Free the dictionary mutex. Do not call unless sure the library
223
 * is not in use anymore !
224
 */
225
void
226
0
xmlDictCleanup(void) {
227
0
    if (!xmlDictInitialized)
228
0
        return;
229
230
0
    xmlFreeMutex(xmlDictMutex);
231
232
0
    xmlDictInitialized = 0;
233
0
}
234
235
/*
236
 * xmlDictAddString:
237
 * @dict: the dictionary
238
 * @name: the name of the userdata
239
 * @len: the length of the name
240
 *
241
 * Add the string to the array[s]
242
 *
243
 * Returns the pointer of the local string, or NULL in case of error.
244
 */
245
static const xmlChar *
246
10.5M
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
247
10.5M
    xmlDictStringsPtr pool;
248
10.5M
    const xmlChar *ret;
249
10.5M
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
250
10.5M
    size_t limit = 0;
251
252
#ifdef DICT_DEBUG_PATTERNS
253
    fprintf(stderr, "-");
254
#endif
255
10.5M
    pool = dict->strings;
256
10.5M
    while (pool != NULL) {
257
8.98M
  if ((size_t)(pool->end - pool->free) > namelen)
258
8.96M
      goto found_pool;
259
14.7k
  if (pool->size > size) size = pool->size;
260
14.7k
        limit += pool->size;
261
14.7k
  pool = pool->next;
262
14.7k
    }
263
    /*
264
     * Not found, need to allocate
265
     */
266
1.59M
    if (pool == NULL) {
267
1.59M
        if ((dict->limit > 0) && (limit > dict->limit)) {
268
0
            return(NULL);
269
0
        }
270
271
1.59M
        if (size == 0) size = 1000;
272
14.1k
  else size *= 4; /* exponential growth */
273
1.59M
        if (size < 4 * namelen)
274
7.51k
      size = 4 * namelen; /* just in case ! */
275
1.59M
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
276
1.59M
  if (pool == NULL)
277
0
      return(NULL);
278
1.59M
  pool->size = size;
279
1.59M
  pool->nbStrings = 0;
280
1.59M
  pool->free = &pool->array[0];
281
1.59M
  pool->end = &pool->array[size];
282
1.59M
  pool->next = dict->strings;
283
1.59M
  dict->strings = pool;
284
#ifdef DICT_DEBUG_PATTERNS
285
        fprintf(stderr, "+");
286
#endif
287
1.59M
    }
288
10.5M
found_pool:
289
10.5M
    ret = pool->free;
290
10.5M
    memcpy(pool->free, name, namelen);
291
10.5M
    pool->free += namelen;
292
10.5M
    *(pool->free++) = 0;
293
10.5M
    pool->nbStrings++;
294
10.5M
    return(ret);
295
1.59M
}
296
297
/*
298
 * xmlDictAddQString:
299
 * @dict: the dictionary
300
 * @prefix: the prefix of the userdata
301
 * @plen: the prefix length
302
 * @name: the name of the userdata
303
 * @len: the length of the name
304
 *
305
 * Add the QName to the array[s]
306
 *
307
 * Returns the pointer of the local string, or NULL in case of error.
308
 */
309
static const xmlChar *
310
xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, unsigned int plen,
311
                 const xmlChar *name, unsigned int namelen)
312
51.5k
{
313
51.5k
    xmlDictStringsPtr pool;
314
51.5k
    const xmlChar *ret;
315
51.5k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
316
51.5k
    size_t limit = 0;
317
318
51.5k
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
319
320
#ifdef DICT_DEBUG_PATTERNS
321
    fprintf(stderr, "=");
322
#endif
323
51.5k
    pool = dict->strings;
324
51.8k
    while (pool != NULL) {
325
51.7k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
326
51.3k
      goto found_pool;
327
358
  if (pool->size > size) size = pool->size;
328
358
        limit += pool->size;
329
358
  pool = pool->next;
330
358
    }
331
    /*
332
     * Not found, need to allocate
333
     */
334
195
    if (pool == NULL) {
335
195
        if ((dict->limit > 0) && (limit > dict->limit)) {
336
0
            return(NULL);
337
0
        }
338
339
195
        if (size == 0) size = 1000;
340
195
  else size *= 4; /* exponential growth */
341
195
        if (size < 4 * (namelen + plen + 1))
342
0
      size = 4 * (namelen + plen + 1); /* just in case ! */
343
195
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
344
195
  if (pool == NULL)
345
0
      return(NULL);
346
195
  pool->size = size;
347
195
  pool->nbStrings = 0;
348
195
  pool->free = &pool->array[0];
349
195
  pool->end = &pool->array[size];
350
195
  pool->next = dict->strings;
351
195
  dict->strings = pool;
352
#ifdef DICT_DEBUG_PATTERNS
353
        fprintf(stderr, "+");
354
#endif
355
195
    }
356
51.5k
found_pool:
357
51.5k
    ret = pool->free;
358
51.5k
    memcpy(pool->free, prefix, plen);
359
51.5k
    pool->free += plen;
360
51.5k
    *(pool->free++) = ':';
361
51.5k
    memcpy(pool->free, name, namelen);
362
51.5k
    pool->free += namelen;
363
51.5k
    *(pool->free++) = 0;
364
51.5k
    pool->nbStrings++;
365
51.5k
    return(ret);
366
195
}
367
368
#ifdef WITH_BIG_KEY
369
/*
370
 * xmlDictComputeBigKey:
371
 *
372
 * Calculate a hash key using a good hash function that works well for
373
 * larger hash table sizes.
374
 *
375
 * Hash function by "One-at-a-Time Hash" see
376
 * http://burtleburtle.net/bob/hash/doobs.html
377
 */
378
379
#ifdef __clang__
380
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
381
#endif
382
static uint32_t
383
3.18M
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
384
3.18M
    uint32_t hash;
385
3.18M
    int i;
386
387
3.18M
    if (namelen <= 0 || data == NULL) return(0);
388
389
3.18M
    hash = seed;
390
391
26.5M
    for (i = 0;i < namelen; i++) {
392
23.3M
        hash += data[i];
393
23.3M
  hash += (hash << 10);
394
23.3M
  hash ^= (hash >> 6);
395
23.3M
    }
396
3.18M
    hash += (hash << 3);
397
3.18M
    hash ^= (hash >> 11);
398
3.18M
    hash += (hash << 15);
399
400
3.18M
    return hash;
401
3.18M
}
402
403
/*
404
 * xmlDictComputeBigQKey:
405
 *
406
 * Calculate a hash key for two strings using a good hash function
407
 * that works well for larger hash table sizes.
408
 *
409
 * Hash function by "One-at-a-Time Hash" see
410
 * http://burtleburtle.net/bob/hash/doobs.html
411
 *
412
 * Neither of the two strings must be NULL.
413
 */
414
#ifdef __clang__
415
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
416
#endif
417
static unsigned long
418
xmlDictComputeBigQKey(const xmlChar *prefix, int plen,
419
                      const xmlChar *name, int len, int seed)
420
22.2k
{
421
22.2k
    uint32_t hash;
422
22.2k
    int i;
423
424
22.2k
    hash = seed;
425
426
90.2k
    for (i = 0;i < plen; i++) {
427
67.9k
        hash += prefix[i];
428
67.9k
  hash += (hash << 10);
429
67.9k
  hash ^= (hash >> 6);
430
67.9k
    }
431
22.2k
    hash += ':';
432
22.2k
    hash += (hash << 10);
433
22.2k
    hash ^= (hash >> 6);
434
435
225k
    for (i = 0;i < len; i++) {
436
202k
        hash += name[i];
437
202k
  hash += (hash << 10);
438
202k
  hash ^= (hash >> 6);
439
202k
    }
440
22.2k
    hash += (hash << 3);
441
22.2k
    hash ^= (hash >> 11);
442
22.2k
    hash += (hash << 15);
443
444
22.2k
    return hash;
445
22.2k
}
446
#endif /* WITH_BIG_KEY */
447
448
/*
449
 * xmlDictComputeFastKey:
450
 *
451
 * Calculate a hash key using a fast hash function that works well
452
 * for low hash table fill.
453
 */
454
static unsigned long
455
35.6M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
456
35.6M
    unsigned long value = seed;
457
458
35.6M
    if (name == NULL) return(0);
459
35.6M
    value += *name;
460
35.6M
    value <<= 5;
461
35.6M
    if (namelen > 10) {
462
5.47M
        value += name[namelen - 1];
463
5.47M
        namelen = 10;
464
5.47M
    }
465
35.6M
    switch (namelen) {
466
6.91M
        case 10: value += name[9];
467
        /* Falls through. */
468
7.80M
        case 9: value += name[8];
469
        /* Falls through. */
470
9.17M
        case 8: value += name[7];
471
        /* Falls through. */
472
11.0M
        case 7: value += name[6];
473
        /* Falls through. */
474
12.4M
        case 6: value += name[5];
475
        /* Falls through. */
476
17.0M
        case 5: value += name[4];
477
        /* Falls through. */
478
20.4M
        case 4: value += name[3];
479
        /* Falls through. */
480
28.6M
        case 3: value += name[2];
481
        /* Falls through. */
482
30.2M
        case 2: value += name[1];
483
        /* Falls through. */
484
35.6M
        default: break;
485
35.6M
    }
486
35.6M
    return(value);
487
35.6M
}
488
489
/*
490
 * xmlDictComputeFastQKey:
491
 *
492
 * Calculate a hash key for two strings using a fast hash function
493
 * that works well for low hash table fill.
494
 *
495
 * Neither of the two strings must be NULL.
496
 */
497
static unsigned long
498
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
499
                       const xmlChar *name, int len, int seed)
500
95.4k
{
501
95.4k
    unsigned long value = seed;
502
503
95.4k
    if (plen == 0)
504
0
  value += 30 * ':';
505
95.4k
    else
506
95.4k
  value += 30 * (*prefix);
507
508
95.4k
    if (len > 10) {
509
6.60k
        int offset = len - (plen + 1 + 1);
510
6.60k
  if (offset < 0)
511
300
      offset = len - (10 + 1);
512
6.60k
  value += name[offset];
513
6.60k
        len = 10;
514
6.60k
  if (plen > 10)
515
476
      plen = 10;
516
6.60k
    }
517
95.4k
    switch (plen) {
518
617
        case 10: value += prefix[9];
519
        /* Falls through. */
520
1.28k
        case 9: value += prefix[8];
521
        /* Falls through. */
522
3.35k
        case 8: value += prefix[7];
523
        /* Falls through. */
524
3.81k
        case 7: value += prefix[6];
525
        /* Falls through. */
526
4.29k
        case 6: value += prefix[5];
527
        /* Falls through. */
528
7.31k
        case 5: value += prefix[4];
529
        /* Falls through. */
530
9.04k
        case 4: value += prefix[3];
531
        /* Falls through. */
532
50.9k
        case 3: value += prefix[2];
533
        /* Falls through. */
534
85.0k
        case 2: value += prefix[1];
535
        /* Falls through. */
536
92.6k
        case 1: value += prefix[0];
537
        /* Falls through. */
538
95.4k
        default: break;
539
95.4k
    }
540
95.4k
    len -= plen;
541
95.4k
    if (len > 0) {
542
77.7k
        value += ':';
543
77.7k
  len--;
544
77.7k
    }
545
95.4k
    switch (len) {
546
0
        case 10: value += name[9];
547
        /* Falls through. */
548
0
        case 9: value += name[8];
549
        /* Falls through. */
550
955
        case 8: value += name[7];
551
        /* Falls through. */
552
2.38k
        case 7: value += name[6];
553
        /* Falls through. */
554
8.16k
        case 6: value += name[5];
555
        /* Falls through. */
556
32.4k
        case 5: value += name[4];
557
        /* Falls through. */
558
47.9k
        case 4: value += name[3];
559
        /* Falls through. */
560
51.9k
        case 3: value += name[2];
561
        /* Falls through. */
562
56.5k
        case 2: value += name[1];
563
        /* Falls through. */
564
67.0k
        case 1: value += name[0];
565
        /* Falls through. */
566
95.4k
        default: break;
567
95.4k
    }
568
95.4k
    return(value);
569
95.4k
}
570
571
/**
572
 * xmlDictCreate:
573
 *
574
 * Create a new dictionary
575
 *
576
 * Returns the newly created dictionary, or NULL if an error occurred.
577
 */
578
xmlDictPtr
579
1.80M
xmlDictCreate(void) {
580
1.80M
    xmlDictPtr dict;
581
582
1.80M
    if (!xmlDictInitialized)
583
0
        if (!__xmlInitializeDict())
584
0
            return(NULL);
585
586
#ifdef DICT_DEBUG_PATTERNS
587
    fprintf(stderr, "C");
588
#endif
589
590
1.80M
    dict = xmlMalloc(sizeof(xmlDict));
591
1.80M
    if (dict) {
592
1.80M
        dict->ref_counter = 1;
593
1.80M
        dict->limit = 0;
594
595
1.80M
        dict->size = MIN_DICT_SIZE;
596
1.80M
  dict->nbElems = 0;
597
1.80M
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
598
1.80M
  dict->strings = NULL;
599
1.80M
  dict->subdict = NULL;
600
1.80M
        if (dict->dict) {
601
1.80M
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
602
#ifdef DICT_RANDOMIZATION
603
            dict->seed = __xmlRandom();
604
#else
605
1.80M
            dict->seed = 0;
606
1.80M
#endif
607
1.80M
      return(dict);
608
1.80M
        }
609
0
        xmlFree(dict);
610
0
    }
611
0
    return(NULL);
612
1.80M
}
613
614
/**
615
 * xmlDictCreateSub:
616
 * @sub: an existing dictionary
617
 *
618
 * Create a new dictionary, inheriting strings from the read-only
619
 * dictionary @sub. On lookup, strings are first searched in the
620
 * new dictionary, then in @sub, and if not found are created in the
621
 * new dictionary.
622
 *
623
 * Returns the newly created dictionary, or NULL if an error occurred.
624
 */
625
xmlDictPtr
626
0
xmlDictCreateSub(xmlDictPtr sub) {
627
0
    xmlDictPtr dict = xmlDictCreate();
628
629
0
    if ((dict != NULL) && (sub != NULL)) {
630
#ifdef DICT_DEBUG_PATTERNS
631
        fprintf(stderr, "R");
632
#endif
633
0
        dict->seed = sub->seed;
634
0
        dict->subdict = sub;
635
0
  xmlDictReference(dict->subdict);
636
0
    }
637
0
    return(dict);
638
0
}
639
640
/**
641
 * xmlDictReference:
642
 * @dict: the dictionary
643
 *
644
 * Increment the reference counter of a dictionary
645
 *
646
 * Returns 0 in case of success and -1 in case of error
647
 */
648
int
649
1.53M
xmlDictReference(xmlDictPtr dict) {
650
1.53M
    if (!xmlDictInitialized)
651
0
        if (!__xmlInitializeDict())
652
0
            return(-1);
653
654
1.53M
    if (dict == NULL) return -1;
655
1.44M
    xmlMutexLock(xmlDictMutex);
656
1.44M
    dict->ref_counter++;
657
1.44M
    xmlMutexUnlock(xmlDictMutex);
658
1.44M
    return(0);
659
1.53M
}
660
661
/**
662
 * xmlDictGrow:
663
 * @dict: the dictionary
664
 * @size: the new size of the dictionary
665
 *
666
 * resize the dictionary
667
 *
668
 * Returns 0 in case of success, -1 in case of failure
669
 */
670
static int
671
12.9k
xmlDictGrow(xmlDictPtr dict, size_t size) {
672
12.9k
    unsigned long key, okey;
673
12.9k
    size_t oldsize, i;
674
12.9k
    xmlDictEntryPtr iter, next;
675
12.9k
    struct _xmlDictEntry *olddict;
676
#ifdef DEBUG_GROW
677
    unsigned long nbElem = 0;
678
#endif
679
12.9k
    int ret = 0;
680
12.9k
    int keep_keys = 1;
681
682
12.9k
    if (dict == NULL)
683
0
  return(-1);
684
12.9k
    if (size < 8)
685
0
        return(-1);
686
12.9k
    if (size > 8 * 2048)
687
0
  return(-1);
688
689
#ifdef DICT_DEBUG_PATTERNS
690
    fprintf(stderr, "*");
691
#endif
692
693
12.9k
    oldsize = dict->size;
694
12.9k
    olddict = dict->dict;
695
12.9k
    if (olddict == NULL)
696
0
        return(-1);
697
12.9k
    if (oldsize == MIN_DICT_SIZE)
698
12.9k
        keep_keys = 0;
699
700
12.9k
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
701
12.9k
    if (dict->dict == NULL) {
702
0
  dict->dict = olddict;
703
0
  return(-1);
704
0
    }
705
12.9k
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
706
12.9k
    dict->size = size;
707
708
    /*  If the two loops are merged, there would be situations where
709
  a new entry needs to allocated and data copied into it from
710
  the main dict. It is nicer to run through the array twice, first
711
  copying all the elements in the main array (less probability of
712
  allocate) and then the rest, so we only free in the second loop.
713
    */
714
1.66M
    for (i = 0; i < oldsize; i++) {
715
1.65M
  if (olddict[i].valid == 0)
716
1.24M
      continue;
717
718
414k
  if (keep_keys)
719
0
      okey = olddict[i].okey;
720
414k
  else
721
414k
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
722
414k
  key = okey % dict->size;
723
724
414k
  if (dict->dict[key].valid == 0) {
725
391k
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
726
391k
      dict->dict[key].next = NULL;
727
391k
      dict->dict[key].okey = okey;
728
391k
  } else {
729
22.4k
      xmlDictEntryPtr entry;
730
731
22.4k
      entry = xmlMalloc(sizeof(xmlDictEntry));
732
22.4k
      if (entry != NULL) {
733
22.4k
    entry->name = olddict[i].name;
734
22.4k
    entry->len = olddict[i].len;
735
22.4k
    entry->okey = okey;
736
22.4k
    entry->next = dict->dict[key].next;
737
22.4k
    entry->valid = 1;
738
22.4k
    dict->dict[key].next = entry;
739
22.4k
      } else {
740
          /*
741
     * we don't have much ways to alert from here
742
     * result is losing an entry and unicity guarantee
743
     */
744
0
          ret = -1;
745
0
      }
746
22.4k
  }
747
#ifdef DEBUG_GROW
748
  nbElem++;
749
#endif
750
414k
    }
751
752
1.66M
    for (i = 0; i < oldsize; i++) {
753
1.65M
  iter = olddict[i].next;
754
1.96M
  while (iter) {
755
310k
      next = iter->next;
756
757
      /*
758
       * put back the entry in the new dict
759
       */
760
761
310k
      if (keep_keys)
762
0
    okey = iter->okey;
763
310k
      else
764
310k
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
765
310k
      key = okey % dict->size;
766
310k
      if (dict->dict[key].valid == 0) {
767
269k
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
768
269k
    dict->dict[key].next = NULL;
769
269k
    dict->dict[key].valid = 1;
770
269k
    dict->dict[key].okey = okey;
771
269k
    xmlFree(iter);
772
269k
      } else {
773
40.9k
    iter->next = dict->dict[key].next;
774
40.9k
    iter->okey = okey;
775
40.9k
    dict->dict[key].next = iter;
776
40.9k
      }
777
778
#ifdef DEBUG_GROW
779
      nbElem++;
780
#endif
781
782
310k
      iter = next;
783
310k
  }
784
1.65M
    }
785
786
12.9k
    xmlFree(olddict);
787
788
#ifdef DEBUG_GROW
789
    xmlGenericError(xmlGenericErrorContext,
790
      "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
791
#endif
792
793
12.9k
    return(ret);
794
12.9k
}
795
796
/**
797
 * xmlDictFree:
798
 * @dict: the dictionary
799
 *
800
 * Free the hash @dict and its contents. The userdata is
801
 * deallocated with @f if provided.
802
 */
803
void
804
3.24M
xmlDictFree(xmlDictPtr dict) {
805
3.24M
    size_t i;
806
3.24M
    xmlDictEntryPtr iter;
807
3.24M
    xmlDictEntryPtr next;
808
3.24M
    int inside_dict = 0;
809
3.24M
    xmlDictStringsPtr pool, nextp;
810
811
3.24M
    if (dict == NULL)
812
0
  return;
813
814
3.24M
    if (!xmlDictInitialized)
815
0
        if (!__xmlInitializeDict())
816
0
            return;
817
818
    /* decrement the counter, it may be shared by a parser and docs */
819
3.24M
    xmlMutexLock(xmlDictMutex);
820
3.24M
    dict->ref_counter--;
821
3.24M
    if (dict->ref_counter > 0) {
822
1.44M
        xmlMutexUnlock(xmlDictMutex);
823
1.44M
        return;
824
1.44M
    }
825
826
1.80M
    xmlMutexUnlock(xmlDictMutex);
827
828
1.80M
    if (dict->subdict != NULL) {
829
0
        xmlDictFree(dict->subdict);
830
0
    }
831
832
1.80M
    if (dict->dict) {
833
160M
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
834
158M
      iter = &(dict->dict[i]);
835
158M
      if (iter->valid == 0)
836
150M
    continue;
837
8.11M
      inside_dict = 1;
838
18.7M
      while (iter) {
839
10.6M
    next = iter->next;
840
10.6M
    if (!inside_dict)
841
2.49M
        xmlFree(iter);
842
10.6M
    dict->nbElems--;
843
10.6M
    inside_dict = 0;
844
10.6M
    iter = next;
845
10.6M
      }
846
8.11M
  }
847
1.80M
  xmlFree(dict->dict);
848
1.80M
    }
849
1.80M
    pool = dict->strings;
850
3.39M
    while (pool != NULL) {
851
1.59M
        nextp = pool->next;
852
1.59M
  xmlFree(pool);
853
1.59M
  pool = nextp;
854
1.59M
    }
855
1.80M
    xmlFree(dict);
856
1.80M
}
857
858
/**
859
 * xmlDictLookup:
860
 * @dict: the dictionary
861
 * @name: the name of the userdata
862
 * @len: the length of the name, if -1 it is recomputed
863
 *
864
 * Add the @name to the dictionary @dict if not present.
865
 *
866
 * Returns the internal copy of the name or NULL in case of internal error
867
 */
868
const xmlChar *
869
38.5M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
870
38.5M
    unsigned long key, okey, nbi = 0;
871
38.5M
    xmlDictEntryPtr entry;
872
38.5M
    xmlDictEntryPtr insert;
873
38.5M
    const xmlChar *ret;
874
38.5M
    unsigned int l;
875
876
38.5M
    if ((dict == NULL) || (name == NULL))
877
533k
  return(NULL);
878
879
38.0M
    if (len < 0)
880
6.30M
        l = strlen((const char *) name);
881
31.7M
    else
882
31.7M
        l = len;
883
884
38.0M
    if (((dict->limit > 0) && (l >= dict->limit)) ||
885
38.0M
        (l > INT_MAX / 2))
886
0
        return(NULL);
887
888
    /*
889
     * Check for duplicate and insertion location.
890
     */
891
38.0M
    okey = xmlDictComputeKey(dict, name, l);
892
38.0M
    key = okey % dict->size;
893
38.0M
    if (dict->dict[key].valid == 0) {
894
7.83M
  insert = NULL;
895
30.2M
    } else {
896
36.4M
  for (insert = &(dict->dict[key]); insert->next != NULL;
897
30.2M
       insert = insert->next) {
898
9.58M
#ifdef __GNUC__
899
9.58M
      if ((insert->okey == okey) && (insert->len == l)) {
900
3.46M
    if (!memcmp(insert->name, name, l))
901
3.41M
        return(insert->name);
902
3.46M
      }
903
#else
904
      if ((insert->okey == okey) && (insert->len == l) &&
905
          (!xmlStrncmp(insert->name, name, l)))
906
    return(insert->name);
907
#endif
908
6.17M
      nbi++;
909
6.17M
  }
910
26.8M
#ifdef __GNUC__
911
26.8M
  if ((insert->okey == okey) && (insert->len == l)) {
912
24.1M
      if (!memcmp(insert->name, name, l))
913
24.0M
    return(insert->name);
914
24.1M
  }
915
#else
916
  if ((insert->okey == okey) && (insert->len == l) &&
917
      (!xmlStrncmp(insert->name, name, l)))
918
      return(insert->name);
919
#endif
920
26.8M
    }
921
922
10.5M
    if (dict->subdict) {
923
0
        unsigned long skey;
924
925
        /* we cannot always reuse the same okey for the subdict */
926
0
        if (((dict->size == MIN_DICT_SIZE) &&
927
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
928
0
            ((dict->size != MIN_DICT_SIZE) &&
929
0
       (dict->subdict->size == MIN_DICT_SIZE)))
930
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
931
0
  else
932
0
      skey = okey;
933
934
0
  key = skey % dict->subdict->size;
935
0
  if (dict->subdict->dict[key].valid != 0) {
936
0
      xmlDictEntryPtr tmp;
937
938
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
939
0
     tmp = tmp->next) {
940
0
#ifdef __GNUC__
941
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
942
0
        if (!memcmp(tmp->name, name, l))
943
0
      return(tmp->name);
944
0
    }
945
#else
946
    if ((tmp->okey == skey) && (tmp->len == l) &&
947
        (!xmlStrncmp(tmp->name, name, l)))
948
        return(tmp->name);
949
#endif
950
0
    nbi++;
951
0
      }
952
0
#ifdef __GNUC__
953
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
954
0
    if (!memcmp(tmp->name, name, l))
955
0
        return(tmp->name);
956
0
      }
957
#else
958
      if ((tmp->okey == skey) && (tmp->len == l) &&
959
    (!xmlStrncmp(tmp->name, name, l)))
960
    return(tmp->name);
961
#endif
962
0
  }
963
0
  key = okey % dict->size;
964
0
    }
965
966
10.5M
    ret = xmlDictAddString(dict, name, l);
967
10.5M
    if (ret == NULL)
968
0
        return(NULL);
969
10.5M
    if (insert == NULL) {
970
7.83M
  entry = &(dict->dict[key]);
971
7.83M
    } else {
972
2.72M
  entry = xmlMalloc(sizeof(xmlDictEntry));
973
2.72M
  if (entry == NULL)
974
0
       return(NULL);
975
2.72M
    }
976
10.5M
    entry->name = ret;
977
10.5M
    entry->len = l;
978
10.5M
    entry->next = NULL;
979
10.5M
    entry->valid = 1;
980
10.5M
    entry->okey = okey;
981
982
983
10.5M
    if (insert != NULL)
984
2.72M
  insert->next = entry;
985
986
10.5M
    dict->nbElems++;
987
988
10.5M
    if ((nbi > MAX_HASH_LEN) &&
989
10.5M
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
990
12.6k
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
991
0
      return(NULL);
992
12.6k
    }
993
    /* Note that entry may have been freed at this point by xmlDictGrow */
994
995
10.5M
    return(ret);
996
10.5M
}
997
998
/**
999
 * xmlDictExists:
1000
 * @dict: the dictionary
1001
 * @name: the name of the userdata
1002
 * @len: the length of the name, if -1 it is recomputed
1003
 *
1004
 * Check if the @name exists in the dictionary @dict.
1005
 *
1006
 * Returns the internal copy of the name or NULL if not found.
1007
 */
1008
const xmlChar *
1009
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
1010
0
    unsigned long key, okey, nbi = 0;
1011
0
    xmlDictEntryPtr insert;
1012
0
    unsigned int l;
1013
1014
0
    if ((dict == NULL) || (name == NULL))
1015
0
  return(NULL);
1016
1017
0
    if (len < 0)
1018
0
        l = strlen((const char *) name);
1019
0
    else
1020
0
        l = len;
1021
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
1022
0
        (l > INT_MAX / 2))
1023
0
        return(NULL);
1024
1025
    /*
1026
     * Check for duplicate and insertion location.
1027
     */
1028
0
    okey = xmlDictComputeKey(dict, name, l);
1029
0
    key = okey % dict->size;
1030
0
    if (dict->dict[key].valid == 0) {
1031
0
  insert = NULL;
1032
0
    } else {
1033
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1034
0
       insert = insert->next) {
1035
0
#ifdef __GNUC__
1036
0
      if ((insert->okey == okey) && (insert->len == l)) {
1037
0
    if (!memcmp(insert->name, name, l))
1038
0
        return(insert->name);
1039
0
      }
1040
#else
1041
      if ((insert->okey == okey) && (insert->len == l) &&
1042
          (!xmlStrncmp(insert->name, name, l)))
1043
    return(insert->name);
1044
#endif
1045
0
      nbi++;
1046
0
  }
1047
0
#ifdef __GNUC__
1048
0
  if ((insert->okey == okey) && (insert->len == l)) {
1049
0
      if (!memcmp(insert->name, name, l))
1050
0
    return(insert->name);
1051
0
  }
1052
#else
1053
  if ((insert->okey == okey) && (insert->len == l) &&
1054
      (!xmlStrncmp(insert->name, name, l)))
1055
      return(insert->name);
1056
#endif
1057
0
    }
1058
1059
0
    if (dict->subdict) {
1060
0
        unsigned long skey;
1061
1062
        /* we cannot always reuse the same okey for the subdict */
1063
0
        if (((dict->size == MIN_DICT_SIZE) &&
1064
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1065
0
            ((dict->size != MIN_DICT_SIZE) &&
1066
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1067
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1068
0
  else
1069
0
      skey = okey;
1070
1071
0
  key = skey % dict->subdict->size;
1072
0
  if (dict->subdict->dict[key].valid != 0) {
1073
0
      xmlDictEntryPtr tmp;
1074
1075
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1076
0
     tmp = tmp->next) {
1077
0
#ifdef __GNUC__
1078
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1079
0
        if (!memcmp(tmp->name, name, l))
1080
0
      return(tmp->name);
1081
0
    }
1082
#else
1083
    if ((tmp->okey == skey) && (tmp->len == l) &&
1084
        (!xmlStrncmp(tmp->name, name, l)))
1085
        return(tmp->name);
1086
#endif
1087
0
    nbi++;
1088
0
      }
1089
0
#ifdef __GNUC__
1090
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1091
0
    if (!memcmp(tmp->name, name, l))
1092
0
        return(tmp->name);
1093
0
      }
1094
#else
1095
      if ((tmp->okey == skey) && (tmp->len == l) &&
1096
    (!xmlStrncmp(tmp->name, name, l)))
1097
    return(tmp->name);
1098
#endif
1099
0
  }
1100
0
    }
1101
1102
    /* not found */
1103
0
    return(NULL);
1104
0
}
1105
1106
/**
1107
 * xmlDictQLookup:
1108
 * @dict: the dictionary
1109
 * @prefix: the prefix
1110
 * @name: the name
1111
 *
1112
 * Add the QName @prefix:@name to the hash @dict if not present.
1113
 *
1114
 * Returns the internal copy of the QName or NULL in case of internal error
1115
 */
1116
const xmlChar *
1117
117k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1118
117k
    unsigned long okey, key, nbi = 0;
1119
117k
    xmlDictEntryPtr entry;
1120
117k
    xmlDictEntryPtr insert;
1121
117k
    const xmlChar *ret;
1122
117k
    unsigned int len, plen, l;
1123
1124
117k
    if ((dict == NULL) || (name == NULL))
1125
0
  return(NULL);
1126
117k
    if (prefix == NULL)
1127
0
        return(xmlDictLookup(dict, name, -1));
1128
1129
117k
    l = len = strlen((const char *) name);
1130
117k
    plen = strlen((const char *) prefix);
1131
117k
    len += 1 + plen;
1132
1133
    /*
1134
     * Check for duplicate and insertion location.
1135
     */
1136
117k
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1137
117k
    key = okey % dict->size;
1138
117k
    if (dict->dict[key].valid == 0) {
1139
40.7k
  insert = NULL;
1140
77.0k
    } else {
1141
104k
  for (insert = &(dict->dict[key]); insert->next != NULL;
1142
77.0k
       insert = insert->next) {
1143
47.4k
      if ((insert->okey == okey) && (insert->len == len) &&
1144
47.4k
          (xmlStrQEqual(prefix, name, insert->name)))
1145
19.5k
    return(insert->name);
1146
27.9k
      nbi++;
1147
27.9k
  }
1148
57.5k
  if ((insert->okey == okey) && (insert->len == len) &&
1149
57.5k
      (xmlStrQEqual(prefix, name, insert->name)))
1150
46.7k
      return(insert->name);
1151
57.5k
    }
1152
1153
51.5k
    if (dict->subdict) {
1154
0
        unsigned long skey;
1155
1156
        /* we cannot always reuse the same okey for the subdict */
1157
0
        if (((dict->size == MIN_DICT_SIZE) &&
1158
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1159
0
            ((dict->size != MIN_DICT_SIZE) &&
1160
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1161
0
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1162
0
  else
1163
0
      skey = okey;
1164
1165
0
  key = skey % dict->subdict->size;
1166
0
  if (dict->subdict->dict[key].valid != 0) {
1167
0
      xmlDictEntryPtr tmp;
1168
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1169
0
     tmp = tmp->next) {
1170
0
    if ((tmp->okey == skey) && (tmp->len == len) &&
1171
0
        (xmlStrQEqual(prefix, name, tmp->name)))
1172
0
        return(tmp->name);
1173
0
    nbi++;
1174
0
      }
1175
0
      if ((tmp->okey == skey) && (tmp->len == len) &&
1176
0
    (xmlStrQEqual(prefix, name, tmp->name)))
1177
0
    return(tmp->name);
1178
0
  }
1179
0
  key = okey % dict->size;
1180
0
    }
1181
1182
51.5k
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1183
51.5k
    if (ret == NULL)
1184
0
        return(NULL);
1185
51.5k
    if (insert == NULL) {
1186
40.7k
  entry = &(dict->dict[key]);
1187
40.7k
    } else {
1188
10.7k
  entry = xmlMalloc(sizeof(xmlDictEntry));
1189
10.7k
  if (entry == NULL)
1190
0
       return(NULL);
1191
10.7k
    }
1192
51.5k
    entry->name = ret;
1193
51.5k
    entry->len = len;
1194
51.5k
    entry->next = NULL;
1195
51.5k
    entry->valid = 1;
1196
51.5k
    entry->okey = okey;
1197
1198
51.5k
    if (insert != NULL)
1199
10.7k
  insert->next = entry;
1200
1201
51.5k
    dict->nbElems++;
1202
1203
51.5k
    if ((nbi > MAX_HASH_LEN) &&
1204
51.5k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1205
249
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1206
    /* Note that entry may have been freed at this point by xmlDictGrow */
1207
1208
51.5k
    return(ret);
1209
51.5k
}
1210
1211
/**
1212
 * xmlDictOwns:
1213
 * @dict: the dictionary
1214
 * @str: the string
1215
 *
1216
 * check if a string is owned by the dictionary
1217
 *
1218
 * Returns 1 if true, 0 if false and -1 in case of error
1219
 * -1 in case of error
1220
 */
1221
int
1222
28.7M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1223
28.7M
    xmlDictStringsPtr pool;
1224
1225
28.7M
    if ((dict == NULL) || (str == NULL))
1226
0
  return(-1);
1227
28.7M
    pool = dict->strings;
1228
44.2M
    while (pool != NULL) {
1229
32.7M
        if ((str >= &pool->array[0]) && (str <= pool->free))
1230
17.2M
      return(1);
1231
15.5M
  pool = pool->next;
1232
15.5M
    }
1233
11.4M
    if (dict->subdict)
1234
0
        return(xmlDictOwns(dict->subdict, str));
1235
11.4M
    return(0);
1236
11.4M
}
1237
1238
/**
1239
 * xmlDictSize:
1240
 * @dict: the dictionary
1241
 *
1242
 * Query the number of elements installed in the hash @dict.
1243
 *
1244
 * Returns the number of elements in the dictionary or
1245
 * -1 in case of error
1246
 */
1247
int
1248
0
xmlDictSize(xmlDictPtr dict) {
1249
0
    if (dict == NULL)
1250
0
  return(-1);
1251
0
    if (dict->subdict)
1252
0
        return(dict->nbElems + dict->subdict->nbElems);
1253
0
    return(dict->nbElems);
1254
0
}
1255
1256
/**
1257
 * xmlDictSetLimit:
1258
 * @dict: the dictionary
1259
 * @limit: the limit in bytes
1260
 *
1261
 * Set a size limit for the dictionary
1262
 * Added in 2.9.0
1263
 *
1264
 * Returns the previous limit of the dictionary or 0
1265
 */
1266
size_t
1267
2.02M
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1268
2.02M
    size_t ret;
1269
1270
2.02M
    if (dict == NULL)
1271
0
  return(0);
1272
2.02M
    ret = dict->limit;
1273
2.02M
    dict->limit = limit;
1274
2.02M
    return(ret);
1275
2.02M
}
1276
1277
/**
1278
 * xmlDictGetUsage:
1279
 * @dict: the dictionary
1280
 *
1281
 * Get how much memory is used by a dictionary for strings
1282
 * Added in 2.9.0
1283
 *
1284
 * Returns the amount of strings allocated
1285
 */
1286
size_t
1287
0
xmlDictGetUsage(xmlDictPtr dict) {
1288
0
    xmlDictStringsPtr pool;
1289
0
    size_t limit = 0;
1290
1291
0
    if (dict == NULL)
1292
0
  return(0);
1293
0
    pool = dict->strings;
1294
0
    while (pool != NULL) {
1295
0
        limit += pool->size;
1296
0
  pool = pool->next;
1297
0
    }
1298
0
    return(limit);
1299
0
}
1300