Coverage Report

Created: 2022-06-08 06:16

/src/libxml2/buf.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * buf.c: memory buffers for libxml2
3
 *
4
 * new buffer structures and entry points to simplify the maintenance
5
 * of libxml2 and ensure we keep good control over memory allocations
6
 * and stay 64 bits clean.
7
 * The new entry point use the xmlBufPtr opaque structure and
8
 * xmlBuf...() counterparts to the old xmlBuf...() functions
9
 *
10
 * See Copyright for the status of this software.
11
 *
12
 * daniel@veillard.com
13
 */
14
15
#define IN_LIBXML
16
#include "libxml.h"
17
18
#include <string.h> /* for memset() only ! */
19
#include <limits.h>
20
#include <ctype.h>
21
#include <stdlib.h>
22
23
#include <libxml/tree.h>
24
#include <libxml/globals.h>
25
#include <libxml/tree.h>
26
#include <libxml/parserInternals.h> /* for XML_MAX_TEXT_LENGTH */
27
#include "buf.h"
28
29
#ifndef SIZE_MAX
30
473k
#define SIZE_MAX ((size_t) -1)
31
#endif
32
33
#define WITH_BUFFER_COMPAT
34
35
/**
36
 * xmlBuf:
37
 *
38
 * A buffer structure. The base of the structure is somehow compatible
39
 * with struct _xmlBuffer to limit risks on application which accessed
40
 * directly the input->buf->buffer structures.
41
 */
42
43
struct _xmlBuf {
44
    xmlChar *content;   /* The buffer content UTF8 */
45
    unsigned int compat_use;    /* for binary compatibility */
46
    unsigned int compat_size;   /* for binary compatibility */
47
    xmlBufferAllocationScheme alloc; /* The realloc method */
48
    xmlChar *contentIO;   /* in IO mode we may have a different base */
49
    size_t use;           /* The buffer size used */
50
    size_t size;    /* The buffer size */
51
    xmlBufferPtr buffer;        /* wrapper for an old buffer */
52
    int error;                  /* an error code if a failure occurred */
53
};
54
55
#ifdef WITH_BUFFER_COMPAT
56
/*
57
 * Macro for compatibility with xmlBuffer to be used after an xmlBuf
58
 * is updated. This makes sure the compat fields are updated too.
59
 */
60
#define UPDATE_COMPAT(buf)            \
61
7.56M
     if (buf->size < INT_MAX) buf->compat_size = buf->size; \
62
7.56M
     else buf->compat_size = INT_MAX;         \
63
7.56M
     if (buf->use < INT_MAX) buf->compat_use = buf->use; \
64
7.56M
     else buf->compat_use = INT_MAX;
65
66
/*
67
 * Macro for compatibility with xmlBuffer to be used in all the xmlBuf
68
 * entry points, it checks that the compat fields have not been modified
69
 * by direct call to xmlBuffer function from code compiled before 2.9.0 .
70
 */
71
#define CHECK_COMPAT(buf)           \
72
43.2M
     if (buf->size != (size_t) buf->compat_size)     \
73
43.2M
         if (buf->compat_size < INT_MAX)       \
74
0
       buf->size = buf->compat_size;       \
75
43.2M
     if (buf->use != (size_t) buf->compat_use)       \
76
43.2M
         if (buf->compat_use < INT_MAX)         \
77
0
       buf->use = buf->compat_use;
78
79
#else /* ! WITH_BUFFER_COMPAT */
80
#define UPDATE_COMPAT(buf)
81
#define CHECK_COMPAT(buf)
82
#endif /* WITH_BUFFER_COMPAT */
83
84
/**
85
 * xmlBufMemoryError:
86
 * @extra:  extra information
87
 *
88
 * Handle an out of memory condition
89
 * To be improved...
90
 */
91
static void
92
xmlBufMemoryError(xmlBufPtr buf, const char *extra)
93
0
{
94
0
    __xmlSimpleError(XML_FROM_BUFFER, XML_ERR_NO_MEMORY, NULL, NULL, extra);
95
0
    if ((buf) && (buf->error == 0))
96
0
        buf->error = XML_ERR_NO_MEMORY;
97
0
}
98
99
/**
100
 * xmlBufOverflowError:
101
 * @extra:  extra information
102
 *
103
 * Handle a buffer overflow error
104
 * To be improved...
105
 */
106
static void
107
xmlBufOverflowError(xmlBufPtr buf, const char *extra)
108
0
{
109
0
    __xmlSimpleError(XML_FROM_BUFFER, XML_BUF_OVERFLOW, NULL, NULL, extra);
110
0
    if ((buf) && (buf->error == 0))
111
0
        buf->error = XML_BUF_OVERFLOW;
112
0
}
113
114
115
/**
116
 * xmlBufCreate:
117
 *
118
 * routine to create an XML buffer.
119
 * returns the new structure.
120
 */
121
xmlBufPtr
122
4.32k
xmlBufCreate(void) {
123
4.32k
    xmlBufPtr ret;
124
125
4.32k
    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
126
4.32k
    if (ret == NULL) {
127
0
  xmlBufMemoryError(NULL, "creating buffer");
128
0
        return(NULL);
129
0
    }
130
4.32k
    ret->use = 0;
131
4.32k
    ret->error = 0;
132
4.32k
    ret->buffer = NULL;
133
4.32k
    ret->size = xmlDefaultBufferSize;
134
4.32k
    UPDATE_COMPAT(ret);
135
4.32k
    ret->alloc = xmlBufferAllocScheme;
136
4.32k
    ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
137
4.32k
    if (ret->content == NULL) {
138
0
  xmlBufMemoryError(ret, "creating buffer");
139
0
  xmlFree(ret);
140
0
        return(NULL);
141
0
    }
142
4.32k
    ret->content[0] = 0;
143
4.32k
    ret->contentIO = NULL;
144
4.32k
    return(ret);
145
4.32k
}
146
147
/**
148
 * xmlBufCreateSize:
149
 * @size: initial size of buffer
150
 *
151
 * routine to create an XML buffer.
152
 * returns the new structure.
153
 */
154
xmlBufPtr
155
360k
xmlBufCreateSize(size_t size) {
156
360k
    xmlBufPtr ret;
157
158
360k
    if (size == SIZE_MAX)
159
0
        return(NULL);
160
360k
    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
161
360k
    if (ret == NULL) {
162
0
  xmlBufMemoryError(NULL, "creating buffer");
163
0
        return(NULL);
164
0
    }
165
360k
    ret->use = 0;
166
360k
    ret->error = 0;
167
360k
    ret->buffer = NULL;
168
360k
    ret->alloc = xmlBufferAllocScheme;
169
360k
    ret->size = (size ? size + 1 : 0);         /* +1 for ending null */
170
360k
    UPDATE_COMPAT(ret);
171
360k
    if (ret->size){
172
319k
        ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
173
319k
        if (ret->content == NULL) {
174
0
      xmlBufMemoryError(ret, "creating buffer");
175
0
            xmlFree(ret);
176
0
            return(NULL);
177
0
        }
178
319k
        ret->content[0] = 0;
179
319k
    } else
180
40.5k
  ret->content = NULL;
181
360k
    ret->contentIO = NULL;
182
360k
    return(ret);
183
360k
}
184
185
/**
186
 * xmlBufDetach:
187
 * @buf:  the buffer
188
 *
189
 * Remove the string contained in a buffer and give it back to the
190
 * caller. The buffer is reset to an empty content.
191
 * This doesn't work with immutable buffers as they can't be reset.
192
 *
193
 * Returns the previous string contained by the buffer.
194
 */
195
xmlChar *
196
36.1k
xmlBufDetach(xmlBufPtr buf) {
197
36.1k
    xmlChar *ret;
198
199
36.1k
    if (buf == NULL)
200
0
        return(NULL);
201
36.1k
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
202
0
        return(NULL);
203
36.1k
    if (buf->buffer != NULL)
204
0
        return(NULL);
205
36.1k
    if (buf->error)
206
0
        return(NULL);
207
208
36.1k
    ret = buf->content;
209
36.1k
    buf->content = NULL;
210
36.1k
    buf->size = 0;
211
36.1k
    buf->use = 0;
212
36.1k
    UPDATE_COMPAT(buf);
213
214
36.1k
    return ret;
215
36.1k
}
216
217
218
/**
219
 * xmlBufCreateStatic:
220
 * @mem: the memory area
221
 * @size:  the size in byte
222
 *
223
 * routine to create an XML buffer from an immutable memory area.
224
 * The area won't be modified nor copied, and is expected to be
225
 * present until the end of the buffer lifetime.
226
 *
227
 * returns the new structure.
228
 */
229
xmlBufPtr
230
0
xmlBufCreateStatic(void *mem, size_t size) {
231
0
    xmlBufPtr ret;
232
233
0
    if (mem == NULL)
234
0
        return(NULL);
235
236
0
    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
237
0
    if (ret == NULL) {
238
0
  xmlBufMemoryError(NULL, "creating buffer");
239
0
        return(NULL);
240
0
    }
241
0
    ret->use = size;
242
0
    ret->size = size;
243
0
    UPDATE_COMPAT(ret);
244
0
    ret->alloc = XML_BUFFER_ALLOC_IMMUTABLE;
245
0
    ret->content = (xmlChar *) mem;
246
0
    ret->error = 0;
247
0
    ret->buffer = NULL;
248
0
    return(ret);
249
0
}
250
251
/**
252
 * xmlBufGetAllocationScheme:
253
 * @buf:  the buffer
254
 *
255
 * Get the buffer allocation scheme
256
 *
257
 * Returns the scheme or -1 in case of error
258
 */
259
int
260
0
xmlBufGetAllocationScheme(xmlBufPtr buf) {
261
0
    if (buf == NULL) {
262
#ifdef DEBUG_BUFFER
263
        xmlGenericError(xmlGenericErrorContext,
264
    "xmlBufGetAllocationScheme: buf == NULL\n");
265
#endif
266
0
        return(-1);
267
0
    }
268
0
    return(buf->alloc);
269
0
}
270
271
/**
272
 * xmlBufSetAllocationScheme:
273
 * @buf:  the buffer to tune
274
 * @scheme:  allocation scheme to use
275
 *
276
 * Sets the allocation scheme for this buffer
277
 *
278
 * returns 0 in case of success and -1 in case of failure
279
 */
280
int
281
xmlBufSetAllocationScheme(xmlBufPtr buf,
282
360k
                          xmlBufferAllocationScheme scheme) {
283
360k
    if ((buf == NULL) || (buf->error != 0)) {
284
#ifdef DEBUG_BUFFER
285
        xmlGenericError(xmlGenericErrorContext,
286
    "xmlBufSetAllocationScheme: buf == NULL or in error\n");
287
#endif
288
0
        return(-1);
289
0
    }
290
360k
    if ((buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) ||
291
360k
        (buf->alloc == XML_BUFFER_ALLOC_IO))
292
0
        return(-1);
293
360k
    if ((scheme == XML_BUFFER_ALLOC_DOUBLEIT) ||
294
360k
        (scheme == XML_BUFFER_ALLOC_EXACT) ||
295
360k
        (scheme == XML_BUFFER_ALLOC_HYBRID) ||
296
360k
        (scheme == XML_BUFFER_ALLOC_IMMUTABLE) ||
297
360k
  (scheme == XML_BUFFER_ALLOC_BOUNDED)) {
298
360k
  buf->alloc = scheme;
299
360k
        if (buf->buffer)
300
0
            buf->buffer->alloc = scheme;
301
360k
        return(0);
302
360k
    }
303
    /*
304
     * Switching a buffer ALLOC_IO has the side effect of initializing
305
     * the contentIO field with the current content
306
     */
307
0
    if (scheme == XML_BUFFER_ALLOC_IO) {
308
0
        buf->alloc = XML_BUFFER_ALLOC_IO;
309
0
        buf->contentIO = buf->content;
310
0
    }
311
0
    return(-1);
312
360k
}
313
314
/**
315
 * xmlBufFree:
316
 * @buf:  the buffer to free
317
 *
318
 * Frees an XML buffer. It frees both the content and the structure which
319
 * encapsulate it.
320
 */
321
void
322
364k
xmlBufFree(xmlBufPtr buf) {
323
364k
    if (buf == NULL) {
324
#ifdef DEBUG_BUFFER
325
        xmlGenericError(xmlGenericErrorContext,
326
    "xmlBufFree: buf == NULL\n");
327
#endif
328
0
  return;
329
0
    }
330
331
364k
    if ((buf->alloc == XML_BUFFER_ALLOC_IO) &&
332
364k
        (buf->contentIO != NULL)) {
333
0
        xmlFree(buf->contentIO);
334
364k
    } else if ((buf->content != NULL) &&
335
364k
        (buf->alloc != XML_BUFFER_ALLOC_IMMUTABLE)) {
336
324k
        xmlFree(buf->content);
337
324k
    }
338
364k
    xmlFree(buf);
339
364k
}
340
341
/**
342
 * xmlBufEmpty:
343
 * @buf:  the buffer
344
 *
345
 * empty a buffer.
346
 */
347
void
348
0
xmlBufEmpty(xmlBufPtr buf) {
349
0
    if ((buf == NULL) || (buf->error != 0)) return;
350
0
    if (buf->content == NULL) return;
351
0
    CHECK_COMPAT(buf)
352
0
    buf->use = 0;
353
0
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) {
354
0
        buf->content = BAD_CAST "";
355
0
    } else if ((buf->alloc == XML_BUFFER_ALLOC_IO) &&
356
0
               (buf->contentIO != NULL)) {
357
0
        size_t start_buf = buf->content - buf->contentIO;
358
359
0
  buf->size += start_buf;
360
0
        buf->content = buf->contentIO;
361
0
        buf->content[0] = 0;
362
0
    } else {
363
0
        buf->content[0] = 0;
364
0
    }
365
0
    UPDATE_COMPAT(buf)
366
0
}
367
368
/**
369
 * xmlBufShrink:
370
 * @buf:  the buffer to dump
371
 * @len:  the number of xmlChar to remove
372
 *
373
 * Remove the beginning of an XML buffer.
374
 * NOTE that this routine behaviour differs from xmlBufferShrink()
375
 * as it will return 0 on error instead of -1 due to size_t being
376
 * used as the return type.
377
 *
378
 * Returns the number of byte removed or 0 in case of failure
379
 */
380
size_t
381
205k
xmlBufShrink(xmlBufPtr buf, size_t len) {
382
205k
    if ((buf == NULL) || (buf->error != 0)) return(0);
383
205k
    CHECK_COMPAT(buf)
384
205k
    if (len == 0) return(0);
385
198k
    if (len > buf->use) return(0);
386
387
198k
    buf->use -= len;
388
198k
    if ((buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) ||
389
198k
        ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL))) {
390
  /*
391
   * we just move the content pointer, but also make sure
392
   * the perceived buffer size has shrunk accordingly
393
   */
394
0
        buf->content += len;
395
0
  buf->size -= len;
396
397
        /*
398
   * sometimes though it maybe be better to really shrink
399
   * on IO buffers
400
   */
401
0
  if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
402
0
      size_t start_buf = buf->content - buf->contentIO;
403
0
      if (start_buf >= buf->size) {
404
0
    memmove(buf->contentIO, &buf->content[0], buf->use);
405
0
    buf->content = buf->contentIO;
406
0
    buf->content[buf->use] = 0;
407
0
    buf->size += start_buf;
408
0
      }
409
0
  }
410
198k
    } else {
411
198k
  memmove(buf->content, &buf->content[len], buf->use);
412
198k
  buf->content[buf->use] = 0;
413
198k
    }
414
198k
    UPDATE_COMPAT(buf)
415
198k
    return(len);
416
198k
}
417
418
/**
419
 * xmlBufGrowInternal:
420
 * @buf:  the buffer
421
 * @len:  the minimum free size to allocate
422
 *
423
 * Grow the available space of an XML buffer, @len is the target value
424
 * Error checking should be done on buf->error since using the return
425
 * value doesn't work that well
426
 *
427
 * Returns 0 in case of error or the length made available otherwise
428
 */
429
static size_t
430
6.76M
xmlBufGrowInternal(xmlBufPtr buf, size_t len) {
431
6.76M
    size_t size;
432
6.76M
    xmlChar *newbuf;
433
434
6.76M
    if ((buf == NULL) || (buf->error != 0)) return(0);
435
6.76M
    CHECK_COMPAT(buf)
436
437
6.76M
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
438
6.76M
    if (len < buf->size - buf->use)
439
6.75M
        return(buf->size - buf->use);
440
8.27k
    if (len > SIZE_MAX - buf->use)
441
0
        return(0);
442
443
8.27k
    if (buf->size > (size_t) len) {
444
7.13k
        size = buf->size > SIZE_MAX / 2 ? SIZE_MAX : buf->size * 2;
445
7.13k
    } else {
446
1.14k
        size = buf->use + len;
447
1.14k
        size = size > SIZE_MAX - 100 ? SIZE_MAX : size + 100;
448
1.14k
    }
449
450
8.27k
    if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) {
451
        /*
452
   * Used to provide parsing limits
453
   */
454
0
        if ((buf->use + len >= XML_MAX_TEXT_LENGTH) ||
455
0
      (buf->size >= XML_MAX_TEXT_LENGTH)) {
456
0
      xmlBufMemoryError(buf, "buffer error: text too long\n");
457
0
      return(0);
458
0
  }
459
0
  if (size >= XML_MAX_TEXT_LENGTH)
460
0
      size = XML_MAX_TEXT_LENGTH;
461
0
    }
462
8.27k
    if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
463
0
        size_t start_buf = buf->content - buf->contentIO;
464
465
0
  newbuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + size);
466
0
  if (newbuf == NULL) {
467
0
      xmlBufMemoryError(buf, "growing buffer");
468
0
      return(0);
469
0
  }
470
0
  buf->contentIO = newbuf;
471
0
  buf->content = newbuf + start_buf;
472
8.27k
    } else {
473
8.27k
  newbuf = (xmlChar *) xmlRealloc(buf->content, size);
474
8.27k
  if (newbuf == NULL) {
475
0
      xmlBufMemoryError(buf, "growing buffer");
476
0
      return(0);
477
0
  }
478
8.27k
  buf->content = newbuf;
479
8.27k
    }
480
8.27k
    buf->size = size;
481
8.27k
    UPDATE_COMPAT(buf)
482
8.27k
    return(buf->size - buf->use);
483
8.27k
}
484
485
/**
486
 * xmlBufGrow:
487
 * @buf:  the buffer
488
 * @len:  the minimum free size to allocate
489
 *
490
 * Grow the available space of an XML buffer, @len is the target value
491
 * This is been kept compatible with xmlBufferGrow() as much as possible
492
 *
493
 * Returns -1 in case of error or the length made available otherwise
494
 */
495
int
496
6.76M
xmlBufGrow(xmlBufPtr buf, int len) {
497
6.76M
    size_t ret;
498
499
6.76M
    if ((buf == NULL) || (len < 0)) return(-1);
500
6.76M
    if (len == 0)
501
0
        return(0);
502
6.76M
    ret = xmlBufGrowInternal(buf, len);
503
6.76M
    if (buf->error != 0)
504
0
        return(-1);
505
6.76M
    return((int) ret);
506
6.76M
}
507
508
/**
509
 * xmlBufDump:
510
 * @file:  the file output
511
 * @buf:  the buffer to dump
512
 *
513
 * Dumps an XML buffer to  a FILE *.
514
 * Returns the number of #xmlChar written
515
 */
516
size_t
517
0
xmlBufDump(FILE *file, xmlBufPtr buf) {
518
0
    size_t ret;
519
520
0
    if ((buf == NULL) || (buf->error != 0)) {
521
#ifdef DEBUG_BUFFER
522
        xmlGenericError(xmlGenericErrorContext,
523
    "xmlBufDump: buf == NULL or in error\n");
524
#endif
525
0
  return(0);
526
0
    }
527
0
    if (buf->content == NULL) {
528
#ifdef DEBUG_BUFFER
529
        xmlGenericError(xmlGenericErrorContext,
530
    "xmlBufDump: buf->content == NULL\n");
531
#endif
532
0
  return(0);
533
0
    }
534
0
    CHECK_COMPAT(buf)
535
0
    if (file == NULL)
536
0
  file = stdout;
537
0
    ret = fwrite(buf->content, sizeof(xmlChar), buf->use, file);
538
0
    return(ret);
539
0
}
540
541
/**
542
 * xmlBufContent:
543
 * @buf:  the buffer
544
 *
545
 * Function to extract the content of a buffer
546
 *
547
 * Returns the internal content
548
 */
549
550
xmlChar *
551
xmlBufContent(const xmlBuf *buf)
552
7.11M
{
553
7.11M
    if ((!buf) || (buf->error))
554
0
        return NULL;
555
556
7.11M
    return(buf->content);
557
7.11M
}
558
559
/**
560
 * xmlBufEnd:
561
 * @buf:  the buffer
562
 *
563
 * Function to extract the end of the content of a buffer
564
 *
565
 * Returns the end of the internal content or NULL in case of error
566
 */
567
568
xmlChar *
569
xmlBufEnd(xmlBufPtr buf)
570
13.7M
{
571
13.7M
    if ((!buf) || (buf->error))
572
0
        return NULL;
573
13.7M
    CHECK_COMPAT(buf)
574
575
13.7M
    return(&buf->content[buf->use]);
576
13.7M
}
577
578
/**
579
 * xmlBufAddLen:
580
 * @buf:  the buffer
581
 * @len:  the size which were added at the end
582
 *
583
 * Sometime data may be added at the end of the buffer without
584
 * using the xmlBuf APIs that is used to expand the used space
585
 * and set the zero terminating at the end of the buffer
586
 *
587
 * Returns -1 in case of error and 0 otherwise
588
 */
589
int
590
6.55M
xmlBufAddLen(xmlBufPtr buf, size_t len) {
591
6.55M
    if ((buf == NULL) || (buf->error))
592
0
        return(-1);
593
6.55M
    CHECK_COMPAT(buf)
594
6.55M
    if (len > (buf->size - buf->use))
595
0
        return(-1);
596
6.55M
    buf->use += len;
597
6.55M
    UPDATE_COMPAT(buf)
598
6.55M
    if (buf->size > buf->use)
599
6.55M
        buf->content[buf->use] = 0;
600
0
    else
601
0
        return(-1);
602
6.55M
    return(0);
603
6.55M
}
604
605
/**
606
 * xmlBufLength:
607
 * @buf:  the buffer
608
 *
609
 * Function to get the length of a buffer
610
 *
611
 * Returns the length of data in the internal content
612
 */
613
614
size_t
615
xmlBufLength(const xmlBufPtr buf)
616
0
{
617
0
    if ((!buf) || (buf->error))
618
0
        return 0;
619
0
    CHECK_COMPAT(buf)
620
621
0
    return(buf->use);
622
0
}
623
624
/**
625
 * xmlBufUse:
626
 * @buf:  the buffer
627
 *
628
 * Function to get the length of a buffer
629
 *
630
 * Returns the length of data in the internal content
631
 */
632
633
size_t
634
xmlBufUse(const xmlBufPtr buf)
635
7.92M
{
636
7.92M
    if ((!buf) || (buf->error))
637
0
        return 0;
638
7.92M
    CHECK_COMPAT(buf)
639
640
7.92M
    return(buf->use);
641
7.92M
}
642
643
/**
644
 * xmlBufAvail:
645
 * @buf:  the buffer
646
 *
647
 * Function to find how much free space is allocated but not
648
 * used in the buffer. It reserves one byte for the NUL
649
 * terminator character that is usually needed, so there is
650
 * no need to subtract 1 from the result anymore.
651
 *
652
 * Returns the amount, or 0 if none or if an error occurred.
653
 */
654
655
size_t
656
xmlBufAvail(const xmlBufPtr buf)
657
6.92M
{
658
6.92M
    if ((!buf) || (buf->error))
659
0
        return 0;
660
6.92M
    CHECK_COMPAT(buf)
661
662
6.92M
    return((buf->size > buf->use) ? (buf->size - buf->use - 1) : 0);
663
6.92M
}
664
665
/**
666
 * xmlBufIsEmpty:
667
 * @buf:  the buffer
668
 *
669
 * Tell if a buffer is empty
670
 *
671
 * Returns 0 if no, 1 if yes and -1 in case of error
672
 */
673
int
674
xmlBufIsEmpty(const xmlBufPtr buf)
675
56.5k
{
676
56.5k
    if ((!buf) || (buf->error))
677
0
        return(-1);
678
56.5k
    CHECK_COMPAT(buf)
679
680
56.5k
    return(buf->use == 0);
681
56.5k
}
682
683
/**
684
 * xmlBufResize:
685
 * @buf:  the buffer to resize
686
 * @size:  the desired size
687
 *
688
 * Resize a buffer to accommodate minimum size of @size.
689
 *
690
 * Returns  0 in case of problems, 1 otherwise
691
 */
692
int
693
xmlBufResize(xmlBufPtr buf, size_t size)
694
42.0k
{
695
42.0k
    size_t newSize;
696
42.0k
    xmlChar* rebuf = NULL;
697
42.0k
    size_t start_buf;
698
699
42.0k
    if ((buf == NULL) || (buf->error))
700
0
        return(0);
701
42.0k
    CHECK_COMPAT(buf)
702
703
42.0k
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
704
42.0k
    if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) {
705
        /*
706
   * Used to provide parsing limits
707
   */
708
0
        if (size >= XML_MAX_TEXT_LENGTH) {
709
0
      xmlBufMemoryError(buf, "buffer error: text too long\n");
710
0
      return(0);
711
0
  }
712
0
    }
713
714
    /* Don't resize if we don't have to */
715
42.0k
    if (size < buf->size)
716
0
        return 1;
717
718
    /* figure out new size */
719
42.0k
    switch (buf->alloc){
720
0
  case XML_BUFFER_ALLOC_IO:
721
42.0k
  case XML_BUFFER_ALLOC_DOUBLEIT:
722
      /*take care of empty case*/
723
42.0k
            if (buf->size == 0) {
724
36.1k
                newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
725
36.1k
            } else {
726
5.84k
                newSize = buf->size;
727
5.84k
            }
728
59.9k
      while (size > newSize) {
729
17.9k
          if (newSize > SIZE_MAX / 2) {
730
0
              xmlBufMemoryError(buf, "growing buffer");
731
0
              return 0;
732
0
          }
733
17.9k
          newSize *= 2;
734
17.9k
      }
735
42.0k
      break;
736
42.0k
  case XML_BUFFER_ALLOC_EXACT:
737
0
            newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
738
0
      break;
739
0
        case XML_BUFFER_ALLOC_HYBRID:
740
0
            if (buf->use < BASE_BUFFER_SIZE)
741
0
                newSize = size;
742
0
            else {
743
0
                newSize = buf->size;
744
0
                while (size > newSize) {
745
0
                    if (newSize > SIZE_MAX / 2) {
746
0
                        xmlBufMemoryError(buf, "growing buffer");
747
0
                        return 0;
748
0
                    }
749
0
                    newSize *= 2;
750
0
                }
751
0
            }
752
0
            break;
753
754
0
  default:
755
0
            newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
756
0
      break;
757
42.0k
    }
758
759
42.0k
    if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
760
0
        start_buf = buf->content - buf->contentIO;
761
762
0
        if (start_buf > newSize) {
763
      /* move data back to start */
764
0
      memmove(buf->contentIO, buf->content, buf->use);
765
0
      buf->content = buf->contentIO;
766
0
      buf->content[buf->use] = 0;
767
0
      buf->size += start_buf;
768
0
  } else {
769
0
      rebuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + newSize);
770
0
      if (rebuf == NULL) {
771
0
    xmlBufMemoryError(buf, "growing buffer");
772
0
    return 0;
773
0
      }
774
0
      buf->contentIO = rebuf;
775
0
      buf->content = rebuf + start_buf;
776
0
  }
777
42.0k
    } else {
778
42.0k
  if (buf->content == NULL) {
779
36.1k
      rebuf = (xmlChar *) xmlMallocAtomic(newSize);
780
36.1k
  } else if (buf->size - buf->use < 100) {
781
882
      rebuf = (xmlChar *) xmlRealloc(buf->content, newSize);
782
4.96k
        } else {
783
      /*
784
       * if we are reallocating a buffer far from being full, it's
785
       * better to make a new allocation and copy only the used range
786
       * and free the old one.
787
       */
788
4.96k
      rebuf = (xmlChar *) xmlMallocAtomic(newSize);
789
4.96k
      if (rebuf != NULL) {
790
4.96k
    memcpy(rebuf, buf->content, buf->use);
791
4.96k
    xmlFree(buf->content);
792
4.96k
    rebuf[buf->use] = 0;
793
4.96k
      }
794
4.96k
  }
795
42.0k
  if (rebuf == NULL) {
796
0
      xmlBufMemoryError(buf, "growing buffer");
797
0
      return 0;
798
0
  }
799
42.0k
  buf->content = rebuf;
800
42.0k
    }
801
42.0k
    buf->size = newSize;
802
42.0k
    UPDATE_COMPAT(buf)
803
804
42.0k
    return 1;
805
42.0k
}
806
807
/**
808
 * xmlBufAdd:
809
 * @buf:  the buffer to dump
810
 * @str:  the #xmlChar string
811
 * @len:  the number of #xmlChar to add
812
 *
813
 * Add a string range to an XML buffer. if len == -1, the length of
814
 * str is recomputed.
815
 *
816
 * Returns 0 successful, a positive error code number otherwise
817
 *         and -1 in case of internal or API error.
818
 */
819
int
820
743k
xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
821
743k
    size_t needSize;
822
823
743k
    if ((str == NULL) || (buf == NULL) || (buf->error))
824
0
  return -1;
825
743k
    CHECK_COMPAT(buf)
826
827
743k
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
828
743k
    if (len < -1) {
829
#ifdef DEBUG_BUFFER
830
        xmlGenericError(xmlGenericErrorContext,
831
    "xmlBufAdd: len < 0\n");
832
#endif
833
0
  return -1;
834
0
    }
835
743k
    if (len == 0) return 0;
836
837
361k
    if (len < 0)
838
2.94k
        len = xmlStrlen(str);
839
840
361k
    if (len < 0) return -1;
841
361k
    if (len == 0) return 0;
842
843
361k
    if ((size_t) len >= buf->size - buf->use) {
844
42.0k
        if ((size_t) len >= SIZE_MAX - buf->use)
845
0
            return(-1);
846
42.0k
        needSize = buf->use + len + 1;
847
42.0k
  if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) {
848
      /*
849
       * Used to provide parsing limits
850
       */
851
0
      if (needSize >= XML_MAX_TEXT_LENGTH) {
852
0
    xmlBufMemoryError(buf, "buffer error: text too long\n");
853
0
    return(-1);
854
0
      }
855
0
  }
856
42.0k
        if (!xmlBufResize(buf, needSize)){
857
0
      xmlBufMemoryError(buf, "growing buffer");
858
0
            return XML_ERR_NO_MEMORY;
859
0
        }
860
42.0k
    }
861
862
361k
    memmove(&buf->content[buf->use], str, len*sizeof(xmlChar));
863
361k
    buf->use += len;
864
361k
    buf->content[buf->use] = 0;
865
361k
    UPDATE_COMPAT(buf)
866
361k
    return 0;
867
361k
}
868
869
/**
870
 * xmlBufCat:
871
 * @buf:  the buffer to add to
872
 * @str:  the #xmlChar string
873
 *
874
 * Append a zero terminated string to an XML buffer.
875
 *
876
 * Returns 0 successful, a positive error code number otherwise
877
 *         and -1 in case of internal or API error.
878
 */
879
int
880
2.94k
xmlBufCat(xmlBufPtr buf, const xmlChar *str) {
881
2.94k
    if ((buf == NULL) || (buf->error))
882
0
        return(-1);
883
2.94k
    CHECK_COMPAT(buf)
884
2.94k
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
885
2.94k
    if (str == NULL) return -1;
886
2.94k
    return xmlBufAdd(buf, str, -1);
887
2.94k
}
888
889
/**
890
 * xmlBufCCat:
891
 * @buf:  the buffer to dump
892
 * @str:  the C char string
893
 *
894
 * Append a zero terminated C string to an XML buffer.
895
 *
896
 * Returns 0 successful, a positive error code number otherwise
897
 *         and -1 in case of internal or API error.
898
 */
899
int
900
0
xmlBufCCat(xmlBufPtr buf, const char *str) {
901
0
    return xmlBufCat(buf, (const xmlChar *) str);
902
0
}
903
904
/**
905
 * xmlBufWriteQuotedString:
906
 * @buf:  the XML buffer output
907
 * @string:  the string to add
908
 *
909
 * routine which manage and grows an output buffer. This one writes
910
 * a quoted or double quoted #xmlChar string, checking first if it holds
911
 * quote or double-quotes internally
912
 *
913
 * Returns 0 if successful, a positive error code number otherwise
914
 *         and -1 in case of internal or API error.
915
 */
916
int
917
0
xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string) {
918
0
    const xmlChar *cur, *base;
919
0
    if ((buf == NULL) || (buf->error))
920
0
        return(-1);
921
0
    CHECK_COMPAT(buf)
922
0
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
923
0
        return(-1);
924
0
    if (xmlStrchr(string, '\"')) {
925
0
        if (xmlStrchr(string, '\'')) {
926
#ifdef DEBUG_BUFFER
927
      xmlGenericError(xmlGenericErrorContext,
928
 "xmlBufWriteQuotedString: string contains quote and double-quotes !\n");
929
#endif
930
0
      xmlBufCCat(buf, "\"");
931
0
            base = cur = string;
932
0
            while(*cur != 0){
933
0
                if(*cur == '"'){
934
0
                    if (base != cur)
935
0
                        xmlBufAdd(buf, base, cur - base);
936
0
                    xmlBufAdd(buf, BAD_CAST "&quot;", 6);
937
0
                    cur++;
938
0
                    base = cur;
939
0
                }
940
0
                else {
941
0
                    cur++;
942
0
                }
943
0
            }
944
0
            if (base != cur)
945
0
                xmlBufAdd(buf, base, cur - base);
946
0
      xmlBufCCat(buf, "\"");
947
0
  }
948
0
        else{
949
0
      xmlBufCCat(buf, "\'");
950
0
            xmlBufCat(buf, string);
951
0
      xmlBufCCat(buf, "\'");
952
0
        }
953
0
    } else {
954
0
        xmlBufCCat(buf, "\"");
955
0
        xmlBufCat(buf, string);
956
0
        xmlBufCCat(buf, "\"");
957
0
    }
958
0
    return(0);
959
0
}
960
961
/**
962
 * xmlBufFromBuffer:
963
 * @buffer: incoming old buffer to convert to a new one
964
 *
965
 * Helper routine to switch from the old buffer structures in use
966
 * in various APIs. It creates a wrapper xmlBufPtr which will be
967
 * used for internal processing until the xmlBufBackToBuffer() is
968
 * issued.
969
 *
970
 * Returns a new xmlBufPtr unless the call failed and NULL is returned
971
 */
972
xmlBufPtr
973
0
xmlBufFromBuffer(xmlBufferPtr buffer) {
974
0
    xmlBufPtr ret;
975
976
0
    if (buffer == NULL)
977
0
        return(NULL);
978
979
0
    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
980
0
    if (ret == NULL) {
981
0
  xmlBufMemoryError(NULL, "creating buffer");
982
0
        return(NULL);
983
0
    }
984
0
    ret->use = buffer->use;
985
0
    ret->size = buffer->size;
986
0
    UPDATE_COMPAT(ret);
987
0
    ret->error = 0;
988
0
    ret->buffer = buffer;
989
0
    ret->alloc = buffer->alloc;
990
0
    ret->content = buffer->content;
991
0
    ret->contentIO = buffer->contentIO;
992
993
0
    return(ret);
994
0
}
995
996
/**
997
 * xmlBufBackToBuffer:
998
 * @buf: new buffer wrapping the old one
999
 *
1000
 * Function to be called once internal processing had been done to
1001
 * update back the buffer provided by the user. This can lead to
1002
 * a failure in case the size accumulated in the xmlBuf is larger
1003
 * than what an xmlBuffer can support on 64 bits (INT_MAX)
1004
 * The xmlBufPtr @buf wrapper is deallocated by this call in any case.
1005
 *
1006
 * Returns the old xmlBufferPtr unless the call failed and NULL is returned
1007
 */
1008
xmlBufferPtr
1009
0
xmlBufBackToBuffer(xmlBufPtr buf) {
1010
0
    xmlBufferPtr ret;
1011
1012
0
    if (buf == NULL)
1013
0
        return(NULL);
1014
0
    CHECK_COMPAT(buf)
1015
0
    if ((buf->error) || (buf->buffer == NULL)) {
1016
0
        xmlBufFree(buf);
1017
0
        return(NULL);
1018
0
    }
1019
1020
0
    ret = buf->buffer;
1021
    /*
1022
     * What to do in case of error in the buffer ???
1023
     */
1024
0
    if (buf->use > INT_MAX) {
1025
        /*
1026
         * Worse case, we really allocated and used more than the
1027
         * maximum allowed memory for an xmlBuffer on this architecture.
1028
         * Keep the buffer but provide a truncated size value.
1029
         */
1030
0
        xmlBufOverflowError(buf, "Used size too big for xmlBuffer");
1031
0
        ret->use = INT_MAX;
1032
0
        ret->size = INT_MAX;
1033
0
    } else if (buf->size > INT_MAX) {
1034
        /*
1035
         * milder case, we allocated more than the maximum allowed memory
1036
         * for an xmlBuffer on this architecture, but used less than the
1037
         * limit.
1038
         * Keep the buffer but provide a truncated size value.
1039
         */
1040
0
        xmlBufOverflowError(buf, "Allocated size too big for xmlBuffer");
1041
0
        ret->use = (int) buf->use;
1042
0
        ret->size = INT_MAX;
1043
0
    } else {
1044
0
        ret->use = (int) buf->use;
1045
0
        ret->size = (int) buf->size;
1046
0
    }
1047
0
    ret->alloc = buf->alloc;
1048
0
    ret->content = buf->content;
1049
0
    ret->contentIO = buf->contentIO;
1050
0
    xmlFree(buf);
1051
0
    return(ret);
1052
0
}
1053
1054
/**
1055
 * xmlBufMergeBuffer:
1056
 * @buf: an xmlBufPtr
1057
 * @buffer: the buffer to consume into @buf
1058
 *
1059
 * The content of @buffer is appended to @buf and @buffer is freed
1060
 *
1061
 * Returns -1 in case of error, 0 otherwise, in any case @buffer is freed
1062
 */
1063
int
1064
0
xmlBufMergeBuffer(xmlBufPtr buf, xmlBufferPtr buffer) {
1065
0
    int ret = 0;
1066
1067
0
    if ((buf == NULL) || (buf->error)) {
1068
0
  xmlBufferFree(buffer);
1069
0
        return(-1);
1070
0
    }
1071
0
    CHECK_COMPAT(buf)
1072
0
    if ((buffer != NULL) && (buffer->content != NULL) &&
1073
0
             (buffer->use > 0)) {
1074
0
        ret = xmlBufAdd(buf, buffer->content, buffer->use);
1075
0
    }
1076
0
    xmlBufferFree(buffer);
1077
0
    return(ret);
1078
0
}
1079
1080
/**
1081
 * xmlBufResetInput:
1082
 * @buf: an xmlBufPtr
1083
 * @input: an xmlParserInputPtr
1084
 *
1085
 * Update the input to use the current set of pointers from the buffer.
1086
 *
1087
 * Returns -1 in case of error, 0 otherwise
1088
 */
1089
int
1090
324k
xmlBufResetInput(xmlBufPtr buf, xmlParserInputPtr input) {
1091
324k
    if ((input == NULL) || (buf == NULL) || (buf->error))
1092
0
        return(-1);
1093
324k
    CHECK_COMPAT(buf)
1094
324k
    input->base = input->cur = buf->content;
1095
324k
    input->end = &buf->content[buf->use];
1096
324k
    return(0);
1097
324k
}
1098
1099
/**
1100
 * xmlBufGetInputBase:
1101
 * @buf: an xmlBufPtr
1102
 * @input: an xmlParserInputPtr
1103
 *
1104
 * Get the base of the @input relative to the beginning of the buffer
1105
 *
1106
 * Returns the size_t corresponding to the displacement
1107
 */
1108
size_t
1109
0
xmlBufGetInputBase(xmlBufPtr buf, xmlParserInputPtr input) {
1110
0
    size_t base;
1111
1112
0
    if ((input == NULL) || (buf == NULL) || (buf->error))
1113
0
        return(-1);
1114
0
    CHECK_COMPAT(buf)
1115
0
    base = input->base - buf->content;
1116
    /*
1117
     * We could do some pointer arithmetic checks but that's probably
1118
     * sufficient.
1119
     */
1120
0
    if (base > buf->size) {
1121
0
        xmlBufOverflowError(buf, "Input reference outside of the buffer");
1122
0
        base = 0;
1123
0
    }
1124
0
    return(base);
1125
0
}
1126
1127
/**
1128
 * xmlBufSetInputBaseCur:
1129
 * @buf: an xmlBufPtr
1130
 * @input: an xmlParserInputPtr
1131
 * @base: the base value relative to the beginning of the buffer
1132
 * @cur: the cur value relative to the beginning of the buffer
1133
 *
1134
 * Update the input to use the base and cur relative to the buffer
1135
 * after a possible reallocation of its content
1136
 *
1137
 * Returns -1 in case of error, 0 otherwise
1138
 */
1139
int
1140
xmlBufSetInputBaseCur(xmlBufPtr buf, xmlParserInputPtr input,
1141
0
                      size_t base, size_t cur) {
1142
0
    if (input == NULL)
1143
0
        return(-1);
1144
0
    if ((buf == NULL) || (buf->error)) {
1145
0
        input->base = input->cur = input->end = BAD_CAST "";
1146
0
        return(-1);
1147
0
    }
1148
0
    CHECK_COMPAT(buf)
1149
0
    input->base = &buf->content[base];
1150
0
    input->cur = input->base + cur;
1151
0
    input->end = &buf->content[buf->use];
1152
0
    return(0);
1153
0
}
1154