Partial buffer slicing.
[SquirrelJME.git] / nanocoat / lib / base / streamByte.c
blobf2b98d6454982c33667caf493c84bfb770816fac
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include <string.h>
12 #include "sjme/stream.h"
13 #include "sjme/alloc.h"
14 #include "sjme/debug.h"
15 #include "sjme/util.h"
17 /**
18 * Byte stream initialization data.
20 * @since 2024/08/12
22 typedef struct sjme_stream_byteInit
24 /** The initial limit. */
25 sjme_jint initialLimit;
27 /** The finish function. */
28 sjme_stream_outputByteArrayFinishFunc finish;
30 /** The data to pass to the finish function. */
31 sjme_pointer finishData;
32 } sjme_stream_byteInit;
34 /**
35 * Contains the specific state for byte array output.
37 * @since 2024/01/09
39 typedef struct sjme_stream_cacheByteArray
41 /** The byte array data. */
42 sjme_jubyte* array;
44 /** The current limit. */
45 sjme_jint limit;
47 /** Whatever value to store, passed on close. */
48 sjme_pointer whatever;
50 /** The function to call when the output is finished. */
51 sjme_stream_outputByteArrayFinishFunc finish;
52 } sjme_stream_cacheByteArray;
54 static sjme_errorCode sjme_stream_outputByteArrayClose(
55 sjme_attrInNotNull sjme_stream_output stream,
56 sjme_attrInNotNull sjme_stream_implState* inImplState)
58 sjme_stream_outputByteArrayFinishFunc finish;
59 sjme_pointer finishData;
60 sjme_stream_resultByteArray result;
61 sjme_errorCode error;
63 if (stream == NULL || inImplState == NULL)
64 return SJME_ERROR_NULL_ARGUMENTS;
66 /* Initialize result. */
67 memset(&result, 0, sizeof(result));
68 result.array = inImplState->buffer;
69 result.length = stream->totalWritten;
70 result.free = SJME_JNI_TRUE;
71 result.whatever = inImplState->handleTwo;
73 /* Recover finisher. */
74 finish = inImplState->handle;
75 finishData = inImplState->handleTwo;
77 /* Call the finish handler, if there is one. */
78 if (finish != NULL)
80 /* Call handler, if it fails just stop. */
81 if (sjme_error_is(error = finish(stream, &result, finishData)))
82 return sjme_error_default(error);
85 /* Are we freeing the array? */
86 if (result.free)
88 if (sjme_error_is(error = sjme_alloc_free(inImplState->buffer)))
89 return sjme_error_default(error);
91 inImplState->buffer = NULL;
94 /* Success! */
95 return SJME_ERROR_NONE;
98 static sjme_errorCode sjme_stream_outputByteArrayInit(
99 sjme_attrInNotNull sjme_stream_output stream,
100 sjme_attrInNotNull sjme_stream_implState* inImplState,
101 sjme_attrInNullable sjme_pointer data)
103 sjme_errorCode error;
104 sjme_pointer initBuf;
105 sjme_stream_byteInit* init;
107 if (stream == NULL || inImplState == NULL || data == NULL)
108 return SJME_ERROR_NULL_ARGUMENTS;
110 /* Recover initializer. */
111 init = data;
113 /* Try to allocate an initial buffer. */
114 initBuf = NULL;
115 if (sjme_error_is(error = sjme_alloc(inImplState->inPool,
116 init->initialLimit, &initBuf)) || initBuf == NULL)
117 goto fail_initBufAlloc;
119 /* Setup state. */
120 inImplState->buffer = initBuf;
121 inImplState->handle = init->finish;
122 inImplState->handleTwo = init->finishData;
123 inImplState->limit = init->initialLimit;
125 /* Success! */
126 return SJME_ERROR_NONE;
128 fail_initBufAlloc:
129 if (initBuf != NULL)
130 sjme_alloc_free(initBuf);
132 return sjme_error_default(error);
135 static sjme_errorCode sjme_stream_outputByteArrayWrite(
136 sjme_attrInNotNull sjme_stream_output stream,
137 sjme_attrInNotNull sjme_stream_implState* inImplState,
138 sjme_attrInNotNull sjme_cpointer buf,
139 sjme_attrInPositiveNonZero sjme_jint length)
141 #define GROW_SIZE 32
142 uintptr_t realBuf;
143 sjme_jint available, desireSize;
144 sjme_errorCode error;
146 if (stream == NULL || buf == NULL)
147 return SJME_ERROR_NULL_ARGUMENTS;
149 realBuf = (uintptr_t)buf;
150 if (length < 0 || (realBuf + length) < realBuf)
151 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
153 /* Not enough bytes to fit into the array buffer? Need to grow it? */
154 available = inImplState->limit - stream->totalWritten;
155 if (length > available)
157 /* Resultant buffer would overflow? Way too big? */
158 desireSize = inImplState->limit + length + GROW_SIZE;
159 if (desireSize < 0)
160 return SJME_ERROR_OUT_OF_MEMORY;
162 /* Reallocate memory here. */
163 if (sjme_error_is(error = sjme_alloc_realloc(
164 &inImplState->buffer, desireSize)))
165 return sjme_error_defaultOr(error,
166 SJME_ERROR_OUT_OF_MEMORY);
168 /* The buffer's limit has now increased. */
169 inImplState->limit = desireSize;
172 /* Copy directly into the array buffer. */
173 /* Note that the caller increases totalWritten. */
174 memmove(&inImplState->buffer[stream->totalWritten],
175 buf, length);
177 /* Success! */
178 return SJME_ERROR_NONE;
179 #undef GROW_SIZE
182 static const sjme_stream_outputFunctions sjme_stream_outputByteArrayFunctions =
184 .close = sjme_stream_outputByteArrayClose,
185 .init = sjme_stream_outputByteArrayInit,
186 .write = sjme_stream_outputByteArrayWrite,
189 sjme_errorCode sjme_stream_outputOpenByteArray(
190 sjme_attrInNotNull sjme_alloc_pool* inPool,
191 sjme_attrOutNotNull sjme_stream_output* outStream,
192 sjme_attrInPositive sjme_jint initialLimit,
193 sjme_attrInNullable sjme_stream_outputByteArrayFinishFunc finish,
194 sjme_attrInNullable sjme_pointer finishData)
196 sjme_stream_byteInit init;
198 if (inPool == NULL || outStream == NULL)
199 return SJME_ERROR_NULL_ARGUMENTS;
201 /* Fallback to a default initial limit if zero. */
202 if (initialLimit == 0)
203 initialLimit = 32;
205 if (initialLimit < 0)
206 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
208 /* Setup initializer. */
209 memset(&init, 0, sizeof(init));
210 init.initialLimit = initialLimit;
211 init.finish = finish;
212 init.finishData = finishData;
214 /* Forward to initialization routine. */
215 return sjme_stream_outputOpen(inPool,
216 outStream,
217 &sjme_stream_outputByteArrayFunctions,
218 &init,
219 NULL);