1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
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 // -------------------------------------------------------------------------*/
12 #include "sjme/stream.h"
13 #include "sjme/alloc.h"
14 #include "sjme/debug.h"
15 #include "sjme/util.h"
18 * Byte stream initialization data.
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
;
35 * Contains the specific state for byte array output.
39 typedef struct sjme_stream_cacheByteArray
41 /** The byte array data. */
44 /** The current 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
;
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. */
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? */
88 if (sjme_error_is(error
= sjme_alloc_free(inImplState
->buffer
)))
89 return sjme_error_default(error
);
91 inImplState
->buffer
= NULL
;
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. */
113 /* Try to allocate an initial buffer. */
115 if (sjme_error_is(error
= sjme_alloc(inImplState
->inPool
,
116 init
->initialLimit
, &initBuf
)) || initBuf
== NULL
)
117 goto fail_initBufAlloc
;
120 inImplState
->buffer
= initBuf
;
121 inImplState
->handle
= init
->finish
;
122 inImplState
->handleTwo
= init
->finishData
;
123 inImplState
->limit
= init
->initialLimit
;
126 return SJME_ERROR_NONE
;
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
)
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
;
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
],
178 return SJME_ERROR_NONE
;
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)
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
,
217 &sjme_stream_outputByteArrayFunctions
,