Coverage Report

Created: 2023-12-14 16:40

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