Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/libktx/lib/memstream.c
9912 views
1
/* -*- tab-width: 4; -*- */
2
/* vi: set sw=2 ts=4 expandtab: */
3
4
/*
5
* Copyright 2010-2020 The Khronos Group Inc.
6
* SPDX-License-Identifier: Apache-2.0
7
*/
8
9
/**
10
* @file
11
* @~English
12
*
13
* @brief Implementation of ktxStream for memory.
14
*
15
* @author Maksim Kolesin, Under Development
16
* @author Georg Kolling, Imagination Technology
17
* @author Mark Callow, HI Corporation
18
*/
19
20
#include <assert.h>
21
#include <string.h>
22
#include <stdlib.h>
23
24
#include "ktx.h"
25
#include "ktxint.h"
26
#include "memstream.h"
27
28
/**
29
* @brief Default allocation size for a ktxMemStream.
30
*/
31
#define KTX_MEM_DEFAULT_ALLOCATED_SIZE 256
32
33
/**
34
* @brief Structure to store information about data allocated for ktxMemStream.
35
*/
36
struct ktxMem
37
{
38
const ktx_uint8_t* robytes;/*!< pointer to read-only data */
39
ktx_uint8_t* bytes; /*!< pointer to rw data. */
40
ktx_size_t alloc_size; /*!< allocated size of the memory block. */
41
ktx_size_t used_size; /*!< bytes used. Effectively the write position. */
42
ktx_off_t pos; /*!< read/write position. */
43
};
44
45
static KTX_error_code ktxMem_expand(ktxMem* pMem, const ktx_size_t size);
46
47
/**
48
* @brief Initialize a ktxMem struct for read-write.
49
*
50
* Memory for the stream data is allocated internally but the
51
* caller is responsible for freeing the memory. A pointer to
52
* the memory can be obtained with ktxMem_getdata().
53
*
54
* @sa ktxMem_getdata.
55
*
56
* @param [in] pMem pointer to the @c ktxMem to initialize.
57
*/
58
static KTX_error_code
59
ktxMem_construct(ktxMem* pMem)
60
{
61
pMem->pos = 0;
62
pMem->alloc_size = 0;
63
pMem->robytes = 0;
64
pMem->bytes = 0;
65
pMem->used_size = 0;
66
return ktxMem_expand(pMem, KTX_MEM_DEFAULT_ALLOCATED_SIZE);
67
}
68
69
/**
70
* @brief Create & initialize a ktxMem struct for read-write.
71
*
72
* @sa ktxMem_construct.
73
*
74
* @param [in,out] ppMem pointer to the location in which to return
75
* a pointer to the newly created @c ktxMem.
76
*
77
* @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
78
*
79
* @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory.
80
*/
81
static KTX_error_code
82
ktxMem_create(ktxMem** ppMem)
83
{
84
ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
85
if (pNewMem) {
86
KTX_error_code result = ktxMem_construct(pNewMem);
87
if (result == KTX_SUCCESS)
88
*ppMem = pNewMem;
89
return result;
90
}
91
else {
92
return KTX_OUT_OF_MEMORY;
93
}
94
}
95
96
/**
97
* @brief Initialize a ktxMem struct for read-only.
98
*
99
* @param [in] pMem pointer to the @c ktxMem to initialize.
100
* @param [in] bytes pointer to the data to be read.
101
* @param [in] numBytes number of bytes of data.
102
*/
103
static void
104
ktxMem_construct_ro(ktxMem* pMem, const void* bytes, ktx_size_t numBytes)
105
{
106
pMem->pos = 0;
107
pMem->robytes = bytes;
108
pMem->bytes = 0;
109
pMem->used_size = numBytes;
110
pMem->alloc_size = numBytes;
111
}
112
113
/**
114
* @brief Create & initialize a ktxMem struct for read-only.
115
*
116
* @sa ktxMem_construct.
117
*
118
* @param [in,out] ppMem pointer to the location in which to return
119
* a pointer to the newly created @c ktxMem.
120
* @param [in] bytes pointer to the data to be read.
121
* @param [in] numBytes number of bytes of data.
122
*
123
* @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
124
*
125
* @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory.
126
*/
127
static KTX_error_code
128
ktxMem_create_ro(ktxMem** ppMem, const void* bytes, ktx_size_t numBytes)
129
{
130
ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
131
if (pNewMem) {
132
ktxMem_construct_ro(pNewMem, bytes, numBytes);
133
*ppMem = pNewMem;
134
return KTX_SUCCESS;
135
}
136
else {
137
return KTX_OUT_OF_MEMORY;
138
}
139
}
140
141
/*
142
* ktxMem_destruct not needed as ktxMem_construct caller is reponsible
143
* for freeing the data written.
144
*/
145
146
/**
147
* @brief Free the memory of a struct ktxMem.
148
*
149
* @param pMem pointer to ktxMem to free.
150
*/
151
static void
152
ktxMem_destroy(ktxMem* pMem, ktx_bool_t freeData)
153
{
154
assert(pMem != NULL);
155
if (freeData) {
156
free(pMem->bytes);
157
}
158
free(pMem);
159
}
160
161
#ifdef KTXMEM_CLEAR_USED
162
/**
163
* @brief Clear the data of a memory stream.
164
*
165
* @param pMem pointer to ktxMem to clear.
166
*/
167
static void
168
ktxMem_clear(ktxMem* pMem)
169
{
170
assert(pMem != NULL);
171
memset(pMem, 0, sizeof(ktxMem));
172
}
173
#endif
174
175
/**
176
* @~English
177
* @brief Expand a ktxMem to fit to a new size.
178
*
179
* @param [in] pMem pointer to ktxMem struct to expand.
180
* @param [in] newsize minimum new size required.
181
*
182
* @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
183
*
184
* @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory.
185
*/
186
static KTX_error_code
187
ktxMem_expand(ktxMem *pMem, const ktx_size_t newsize)
188
{
189
ktx_size_t new_alloc_size;
190
191
assert(pMem != NULL && newsize != 0);
192
193
new_alloc_size = pMem->alloc_size == 0 ?
194
KTX_MEM_DEFAULT_ALLOCATED_SIZE : pMem->alloc_size;
195
while (new_alloc_size < newsize) {
196
ktx_size_t alloc_size = new_alloc_size;
197
new_alloc_size <<= 1;
198
if (new_alloc_size < alloc_size) {
199
/* Overflow. Set to maximum size. newsize can't be larger. */
200
new_alloc_size = (ktx_size_t)-1L;
201
}
202
}
203
204
if (new_alloc_size == pMem->alloc_size)
205
return KTX_SUCCESS;
206
207
if (!pMem->bytes)
208
pMem->bytes = (ktx_uint8_t*)malloc(new_alloc_size);
209
else
210
pMem->bytes = (ktx_uint8_t*)realloc(pMem->bytes, new_alloc_size);
211
212
if (!pMem->bytes)
213
{
214
pMem->alloc_size = 0;
215
pMem->used_size = 0;
216
return KTX_OUT_OF_MEMORY;
217
}
218
219
pMem->alloc_size = new_alloc_size;
220
return KTX_SUCCESS;
221
}
222
223
/**
224
* @~English
225
* @brief Read bytes from a ktxMemStream.
226
*
227
* @param [in] str pointer to ktxMem struct, converted to a void*, that
228
* specifies an input stream.
229
* @param [in,out] dst pointer to memory where to copy read bytes.
230
* @param [in,out] count pointer to number of bytes to read.
231
*
232
* @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
233
*
234
* @exception KTX_INVALID_VALUE @p str or @p dst is @c NULL or @p str->data is
235
* @c NULL.
236
* @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
237
*/
238
static
239
KTX_error_code ktxMemStream_read(ktxStream* str, void* dst, const ktx_size_t count)
240
{
241
ktxMem* mem;
242
ktx_off_t newpos;
243
const ktx_uint8_t* bytes;
244
245
246
if (!str || (mem = str->data.mem)== 0)
247
return KTX_INVALID_VALUE;
248
249
newpos = mem->pos + count;
250
/* The first clause checks for overflow. */
251
if (newpos < mem->pos || (ktx_size_t)newpos > mem->used_size)
252
return KTX_FILE_UNEXPECTED_EOF;
253
254
bytes = mem->robytes ? mem->robytes : mem->bytes;
255
memcpy(dst, bytes + mem->pos, count);
256
mem->pos = newpos;
257
258
return KTX_SUCCESS;
259
}
260
261
/**
262
* @~English
263
* @brief Skip bytes in a ktxMemStream.
264
*
265
* @param [in] str pointer to the ktxStream on which to operate.
266
* @param [in] count number of bytes to skip.
267
*
268
* @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
269
*
270
* @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or sufficient
271
* data is not available in ktxMem.
272
* @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
273
*/
274
static
275
KTX_error_code ktxMemStream_skip(ktxStream* str, const ktx_size_t count)
276
{
277
ktxMem* mem;
278
ktx_off_t newpos;
279
280
if (!str || (mem = str->data.mem) == 0)
281
return KTX_INVALID_VALUE;
282
283
newpos = mem->pos + count;
284
/* The first clause checks for overflow. */
285
if (newpos < mem->pos || (ktx_size_t)newpos > mem->used_size)
286
return KTX_FILE_UNEXPECTED_EOF;
287
288
mem->pos = newpos;
289
290
return KTX_SUCCESS;
291
}
292
293
/**
294
* @~English
295
* @brief Write bytes to a ktxMemStream.
296
*
297
* @param [out] str pointer to the ktxStream that specifies the destination.
298
* @param [in] src pointer to the array of elements to be written,
299
* converted to a const void*.
300
* @param [in] size size in bytes of each element to be written.
301
* @param [in] count number of elements, each one with a @p size of size
302
* bytes.
303
*
304
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
305
*
306
* @exception KTX_FILE_OVERFLOW write would result in file exceeding the
307
* maximum permissible size.
308
* @exception KTX_INVALID_OPERATION @p str is a read-only stream.
309
* @exception KTX_INVALID_VALUE @p dst is @c NULL or @p mem is @c NULL.
310
* @exception KTX_OUT_OF_MEMORY See ktxMem_expand() for causes.
311
*/
312
static
313
KTX_error_code ktxMemStream_write(ktxStream* str, const void* src,
314
const ktx_size_t size, const ktx_size_t count)
315
{
316
ktxMem* mem;
317
KTX_error_code rc = KTX_SUCCESS;
318
ktx_size_t new_size;
319
320
if (!str || (mem = str->data.mem) == 0)
321
return KTX_INVALID_VALUE;
322
323
if (mem->robytes)
324
return KTX_INVALID_OPERATION; /* read-only */
325
326
new_size = mem->pos + (size*count);
327
//if (new_size < mem->used_size)
328
if ((ktx_off_t)new_size < mem->pos)
329
return KTX_FILE_OVERFLOW;
330
331
if (mem->alloc_size < new_size) {
332
rc = ktxMem_expand(mem, new_size);
333
if (rc != KTX_SUCCESS)
334
return rc;
335
}
336
337
memcpy(mem->bytes + mem->pos, src, size*count);
338
mem->pos += size*count;
339
if (mem->pos > (ktx_off_t)mem->used_size)
340
mem->used_size = mem->pos;
341
342
343
return KTX_SUCCESS;
344
}
345
346
/**
347
* @~English
348
* @brief Get the current read/write position in a ktxMemStream.
349
*
350
* @param [in] str pointer to the ktxStream to query.
351
* @param [in,out] off pointer to variable to receive the offset value.
352
*
353
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
354
*
355
* @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL.
356
*/
357
static
358
KTX_error_code ktxMemStream_getpos(ktxStream* str, ktx_off_t* const pos)
359
{
360
if (!str || !pos)
361
return KTX_INVALID_VALUE;
362
363
assert(str->type == eStreamTypeMemory);
364
365
*pos = str->data.mem->pos;
366
return KTX_SUCCESS;
367
}
368
369
/**
370
* @~English
371
* @brief Set the current read/write position in a ktxMemStream.
372
*
373
* Offset of 0 is the start of the file.
374
*
375
* @param [in] str pointer to the ktxStream whose r/w position is to be set.
376
* @param [in] off pointer to the offset value to set.
377
*
378
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
379
*
380
* @exception KTX_INVALID_VALUE @p str is @c NULL.
381
* @exception KTX_INVALID_OPERATION @p pos > size of the allocated memory.
382
*/
383
static
384
KTX_error_code ktxMemStream_setpos(ktxStream* str, ktx_off_t pos)
385
{
386
if (!str)
387
return KTX_INVALID_VALUE;
388
389
assert(str->type == eStreamTypeMemory);
390
391
if (pos < 0 || (ktx_size_t)pos > str->data.mem->alloc_size)
392
return KTX_INVALID_OPERATION;
393
394
str->data.mem->pos = pos;
395
return KTX_SUCCESS;
396
}
397
398
/**
399
* @~English
400
* @brief Get a pointer to a ktxMemStream's data.
401
*
402
* Gets a pointer to data that has been written to the stream. Returned
403
* pointer will be 0 if stream is read-only.
404
*
405
* @param [in] str pointer to the ktxStream whose data pointer is to
406
* be queried.
407
* @param [in,out] ppBytes pointer to a variable in which the data pointer
408
* will be written.
409
*
410
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
411
*
412
* @exception KTX_INVALID_VALUE @p str or @p ppBytes is @c NULL.
413
*/
414
KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes)
415
{
416
if (!str || !ppBytes)
417
return KTX_INVALID_VALUE;
418
419
assert(str->type == eStreamTypeMemory);
420
421
*ppBytes = str->data.mem->bytes;
422
return KTX_SUCCESS;
423
}
424
425
/**
426
* @~English
427
* @brief Get the size of a ktxMemStream in bytes.
428
*
429
* @param [in] str pointer to the ktxStream whose size is to be queried.
430
* @param [in,out] size pointer to a variable in which size will be written.
431
*
432
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
433
*
434
* @exception KTX_INVALID_VALUE @p str or @p pSize is @c NULL.
435
*/
436
static
437
KTX_error_code ktxMemStream_getsize(ktxStream* str, ktx_size_t* pSize)
438
{
439
if (!str || !pSize)
440
return KTX_INVALID_VALUE;
441
442
assert(str->type == eStreamTypeMemory);
443
444
*pSize = str->data.mem->used_size;
445
return KTX_SUCCESS;
446
}
447
448
/**
449
* @~English
450
* @brief Setup ktxMemStream function pointers.
451
*/
452
void
453
ktxMemStream_setup(ktxStream* str)
454
{
455
str->type = eStreamTypeMemory;
456
str->read = ktxMemStream_read;
457
str->skip = ktxMemStream_skip;
458
str->write = ktxMemStream_write;
459
str->getpos = ktxMemStream_getpos;
460
str->setpos = ktxMemStream_setpos;
461
str->getsize = ktxMemStream_getsize;
462
str->destruct = ktxMemStream_destruct;
463
}
464
465
/**
466
* @~English
467
* @brief Initialize a read-write ktxMemStream.
468
*
469
* Memory is allocated as data is written. The caller of this is
470
* responsible for freeing this memory unless @a freeOnDestruct
471
* is not KTX_FALSE.
472
*
473
* @param [in] str pointer to a ktxStream struct to initialize.
474
* @param [in] freeOnDestruct If not KTX_FALSE memory holding the data will
475
* be freed by the destructor.
476
*
477
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
478
*
479
* @exception KTX_INVALID_VALUE @p str is @c NULL.
480
* @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory.
481
*/
482
KTX_error_code ktxMemStream_construct(ktxStream* str,
483
ktx_bool_t freeOnDestruct)
484
{
485
ktxMem* mem;
486
KTX_error_code result = KTX_SUCCESS;
487
488
if (!str)
489
return KTX_INVALID_VALUE;
490
491
result = ktxMem_create(&mem);
492
493
if (KTX_SUCCESS == result) {
494
str->data.mem = mem;
495
ktxMemStream_setup(str);
496
str->closeOnDestruct = freeOnDestruct;
497
}
498
499
return result;
500
}
501
502
/**
503
* @~English
504
* @brief Initialize a read-only ktxMemStream.
505
*
506
* @param [in] str pointer to a ktxStream struct to initialize.
507
* @param [in] bytes pointer to an array of bytes containing the data.
508
* @param [in] numBytes size of array of data for ktxMemStream.
509
*
510
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
511
*
512
* @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or @p numBytes
513
* is 0.
514
* or @p size is less than 0.
515
* @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory.
516
*/
517
KTX_error_code ktxMemStream_construct_ro(ktxStream* str,
518
const ktx_uint8_t* bytes,
519
const ktx_size_t numBytes)
520
{
521
ktxMem* mem;
522
KTX_error_code result = KTX_SUCCESS;
523
524
if (!str || !bytes || numBytes == 0)
525
return KTX_INVALID_VALUE;
526
527
result = ktxMem_create_ro(&mem, bytes, numBytes);
528
529
if (KTX_SUCCESS == result) {
530
str->data.mem = mem;
531
ktxMemStream_setup(str);
532
str->closeOnDestruct = KTX_FALSE;
533
}
534
535
return result;
536
}
537
538
/**
539
* @~English
540
* @brief Free the memory used by a ktxMemStream.
541
*
542
* This only frees the memory used to store the data written to the stream,
543
* if the @c freeOnDestruct parameter to ktxMemStream_construct() was not
544
* @c KTX_FALSE. Otherwise it is the responsibility of the caller of
545
* ktxMemStream_construct() and a pointer to this memory should be retrieved
546
* using ktxMemStream_getdata() before calling this function.
547
*
548
* @sa ktxMemStream_construct, ktxMemStream_getdata.
549
*
550
* @param [in] str pointer to the ktxStream whose memory is
551
* to be freed.
552
*/
553
void
554
ktxMemStream_destruct(ktxStream* str)
555
{
556
assert(str && str->type == eStreamTypeMemory);
557
558
ktxMem_destroy(str->data.mem, str->closeOnDestruct);
559
str->data.mem = NULL;
560
}
561
562
563