Do not install wrong cache routine if called before SysBase is complete.
[AROS.git] / workbench / libs / iffparse / bufferingfuncs.c
blobd2900520efd1ab461e6d33fa2dee0718ddd70e64
1 /*
2 Copyright © 1995-2004, The AROS Development Team. All rights reserved.
3 $Id$
5 Funtions needed for buffering writes.
6 */
7 #include "iffparse_intern.h"
11 /****************/
12 /* AllocBuffer */
13 /****************/
15 struct BufferList *AllocBuffer(ULONG bufnodesize,
16 struct IFFParseBase_intern * IFFParseBase)
18 struct BufferList *buflist;
19 struct BufferNode *bufnode;
21 DEBUG_ALLOCBUFFER(dprintf("AllocBuffer: bufnodesize 0x%lx\n", bufnodesize));
23 /* First allocate memory for the BufferList structure */
25 buflist = AllocMem(sizeof (struct BufferList), MEMF_ANY);
27 if (buflist)
29 /* Initialize teh bufferlist */
30 NewList( (struct List*)&(buflist->bl_ListHeader) );
31 buflist->bl_BufferNodeSize = bufnodesize;
34 /* The bufferlist should always contain at least one buffer */
36 bufnode = AllocBufferNode(buflist, IFFParseBase);
38 if (bufnode)
40 buflist->bl_CurrentNode = bufnode;
41 buflist->bl_CurrentOffset = 0;
42 buflist->bl_BufferSize = 0;
43 buflist->bl_CurrentNodeNum = 1;
45 DEBUG_ALLOCBUFFER(dprintf("AllocBuffer: return %p\n", buflist));
46 return (buflist);
49 FreeMem(buflist, sizeof (struct BufferList) );
52 DEBUG_ALLOCBUFFER(dprintf("AllocBuffer: return NULL\n"));
53 return NULL;
57 /**************/
58 /* FreeBuffer */
59 /**************/
61 /* Frees all the buffers and buffernodes inside a bufferlist */
63 VOID FreeBuffer(struct BufferList *buflist,
64 struct IFFParseBase_intern * IFFParseBase)
66 /* Frees all the buffernodes in a bufferlist */
67 struct BufferNode *node, *nextnode;
69 /* first free all the nodes */
71 node = (struct BufferNode*)buflist->bl_ListHeader.mlh_Head;
73 while ((nextnode=(struct BufferNode*)node->bn_Node.mln_Succ))
76 FreeMem(node->bn_BufferPtr, buflist->bl_BufferNodeSize);
78 FreeMem(node,sizeof(struct BufferNode));
79 node = nextnode;
82 /* Free the bufferlist itself */
84 FreeMem(buflist, sizeof (struct BufferList));
85 return;
89 /********************/
90 /* AllocBufferNode */
91 /********************/
93 /* Allocate a new buffernode,
94 a new buffer, and add the buffernode to the TAIL of the bufferlist
96 struct BufferNode *AllocBufferNode(struct BufferList *buflist,
97 struct IFFParseBase_intern * IFFParseBase)
99 /* New buffernodes are added at the HEAD of the list */
101 struct BufferNode *n;
103 DEBUG_ALLOCBUFFER(dprintf("AllocBufferNode: buflist %p\n", buflist));
105 /* first allocate node */
106 if ((n=(struct BufferNode*)AllocMem(sizeof(struct BufferNode),MEMF_ANY)))
108 /* Then allocate buffer */
109 if ((n->bn_BufferPtr=(UBYTE*)AllocMem(buflist->bl_BufferNodeSize, MEMF_ANY)))
111 AddTail((struct List*)buflist,(struct Node*)n);
113 DEBUG_ALLOCBUFFER(dprintf("AllocBufferNode: return %p\n", n));
114 return (n);
117 FreeMem(n,sizeof(struct BufferNode));
120 DEBUG_ALLOCBUFFER(dprintf("AllocBufferNode: return NULL\n"));
121 return NULL;
124 /********************/
125 /* WriteToBuffer */
126 /********************/
128 /* Copies a number of bytes into the buffer at the current position */
130 LONG WriteToBuffer( struct BufferList *buflist, UBYTE *mem, LONG size,
131 struct IFFParseBase_intern * IFFParseBase)
133 /* The buffernode that the routine currently will buffer to */
134 struct BufferNode *bufnode;
137 /* Total number of bytes to be written */
138 ULONG bytes2write;
140 /* Bytes left in bufnode that may be written into */
141 ULONG bytesleft;
142 /* The offset int bufnode that the routine will start buffering at */
143 ULONG bufoffset;
145 ULONG bufnodenum;
147 This is the offset in bytes into the whole buffer, not obly into the
148 current node
150 ULONG offset_into_bigbuf;
153 DEBUG_WRITETOBUFFER(dprintf("WriteToBuffer: buflist %p mem %p size %ld\n", buflist, mem, size));
155 bytes2write = size;
157 /* Get pointer to current buffer in list */
158 bufnode = buflist->bl_CurrentNode;
159 bufoffset = buflist->bl_CurrentOffset;
161 bufnodenum = buflist->bl_CurrentNodeNum;
163 /* Calculate the offset into the buffer as a whole */
164 offset_into_bigbuf = (bufnodenum - 1) * buflist->bl_BufferNodeSize + bufoffset;
166 while (bytes2write)
169 /* Find out how many bytes we can write into the current node */
171 bytesleft = buflist->bl_BufferNodeSize - bufoffset;
174 if (bytes2write > bytesleft)
176 /* Copy into the old buffer all that there is place for */
177 CopyMem(mem,bufnode->bn_BufferPtr + bufoffset, bytesleft);
179 /* We have written bytesleft bytes */
180 bytes2write -= bytesleft;
181 mem += bytesleft;
183 /* No more space in this buffernode */
184 bytesleft = 0;
186 /* Go to the next buffer in the list */
187 bufnode = (struct BufferNode*)bufnode->bn_Node.mln_Succ;
189 bufnodenum ++;
190 /* We go to the start of the current buffernode */
191 bufoffset = 0;
193 if (!bufnode->bn_Node.mln_Succ)
195 /* No more nodes in the list. We have to allocate and add a new one */
198 bufnode = AllocBufferNode(buflist, IFFParseBase);
200 /* Did the allocation succeed ? */
201 if (!bufnode)
203 /* Return number of bytes written so far */
204 DEBUG_WRITETOBUFFER(dprintf("WriteToBuffer: return %ld\n", (size - bytes2write)));
205 return (size - bytes2write);
210 else
212 /* There is place enough to write the remaining bytes into the current buffer */
213 CopyMem
215 mem,
216 bufnode->bn_BufferPtr + bufoffset,
217 bytes2write
220 bufoffset += bytes2write;
222 bytes2write = 0;
225 } /* End of while */
227 /* If we have reached here, we can be sure that bytes written == size
228 (everything has been succesfull) */
230 /* Update some stuff */
231 buflist->bl_CurrentNode = bufnode;
232 buflist->bl_CurrentOffset = bufoffset;
233 buflist->bl_CurrentNodeNum = bufnodenum;
235 /* Update the offset into buffer as a whole */
236 offset_into_bigbuf += size;
238 /* Have we expanded the buffer ? */
239 if (offset_into_bigbuf > buflist->bl_BufferSize)
240 /* Update the size of the buffer */
241 buflist->bl_BufferSize = offset_into_bigbuf;
243 DEBUG_WRITETOBUFFER(dprintf("WriteToBuffer: return %ld\n", size));
244 return (size);
247 /****************/
248 /* SeekBuffer */
249 /****************/
252 BOOL SeekBuffer
254 struct BufferList *buflist,
255 LONG offset
259 struct BufferNode *bufnode;
261 /* The number of bytes we potentially can seek in this buffernode's buffer
263 ULONG left2seekinbuf,
264 /* The size of each buffer. (same for all buffers) */
265 bufnodesize,
266 bufoffset,
267 bufnodenum;
269 LONG offset_into_bigbuf,
270 new_offset_into_bigbuf;
272 DEBUG_SEEKBUFFER(dprintf("WriteToBuffer: buflist %p offset %ld\n", buflist, offset));
274 /* Get the size of the buffers */
275 bufnodesize = buflist->bl_BufferNodeSize;
276 bufnode = buflist->bl_CurrentNode;
277 bufoffset = buflist->bl_CurrentOffset;
278 bufnodenum = buflist->bl_CurrentNodeNum;
280 offset_into_bigbuf = (bufnodenum - 1) * bufnodesize + bufoffset;
282 /* Calculate the new offset into whole buffer. Remember: If the offset is negative,
283 this will be substarction.*/
284 new_offset_into_bigbuf = offset_into_bigbuf + offset;
286 /* Are we seeking forwards, backwords or not at all ? */
287 if (offset > 0)
289 /* Forwards seek */
291 /* Are we trying to seek outside the bounds of the buffer */
292 if (new_offset_into_bigbuf > buflist->bl_BufferSize)
294 DEBUG_SEEKBUFFER(dprintf("WriteToBuffer: return FALSE #1\n"));
295 return (FALSE);
298 /* How much is there left to seek towards the end of the first buf ? */
299 left2seekinbuf = bufnodesize - bufoffset;
301 while (offset) /* While there are more bytes to seek */
303 if (offset > left2seekinbuf)
305 /* We have to jump to the next buffer in the list */
306 bufnode = (struct BufferNode*)bufnode->bn_Node.mln_Succ;
308 #if 0
309 if (!bufnode->bn_Node.mln_Succ)
311 /* Oops !! No more buffers to seek in */
312 DEBUG_SEEKBUFFER(dprintf("WriteToBuffer: return FALSE #2\n"));
313 return (FALSE);
315 #endif
317 offset -= left2seekinbuf;
318 bufnodenum ++;
320 /* Current offset is at start of the buffer */
321 bufoffset = 0;
323 /* The number of bytes written to this buffer
324 is the number we can potentially seek.
326 left2seekinbuf = bufnodesize;
329 else
331 /* We are at the last buffernode we have to seek. */
332 bufoffset += offset;
334 /* not more to seek */
335 offset = 0;
337 } /* End of while */
340 else if (offset < 0)
343 /* Backwards seek */
345 /* Are we trying to seek outside the bounds of the buffer */
346 if (new_offset_into_bigbuf < 0)
348 DEBUG_SEEKBUFFER(dprintf("WriteToBuffer: return FALSE #3\n"));
349 return (FALSE);
352 /* For simplicity we take the absolute value of offset */
353 offset = abs(offset);
355 /* How much is there left to seek towards the start of the first buf ? */
356 left2seekinbuf = bufoffset;
358 while (offset) /* While there are more bytes to seek */
360 if (offset > left2seekinbuf)
363 /* We have to jump to the next buffer in the list */
364 bufnode = (struct BufferNode*)bufnode->bn_Node.mln_Pred;
366 #if 0
367 if (bufnode->bn_Node.mln_Pred == NULL )
369 /* Oops !! No more buffers to seek in */
370 DEBUG_SEEKBUFFER(dprintf("WriteToBuffer: return FALSE #4\n"));
371 return (FALSE);
373 #endif
375 offset -= left2seekinbuf;
376 bufnodenum --;
378 /* Current offset is at end of the buffer */
379 bufoffset = bufnodesize;
381 /* The number of bytes written to this buffer
382 is the number we can potentially seek.
384 left2seekinbuf = bufoffset;
387 else
389 /* We are at the last buffernode we have to seek. */
390 bufoffset -= offset;
391 offset = 0;
393 /* not more to seek */
396 } /* End of while */
399 /* if offset is 0, we are finished seeking */
401 buflist->bl_CurrentNode = bufnode;
402 buflist->bl_CurrentOffset = bufoffset;
403 buflist->bl_CurrentNodeNum = bufnodenum;
405 DEBUG_SEEKBUFFER(dprintf("WriteToBuffer: return TRUE\n"));
406 return (TRUE);
409 /******************/
410 /* BufferToStream */
411 /******************/
413 /* Writes a whole buffer to a stream.
414 It is VERY important that intiff->BufferStartDepth is 0, when this
415 function is called. Else, WriteStream will buffer the writes, and we will
416 get a nice loop allocating ALL the memory of the machine. (imagine that with VM ;-)
418 Well, I guess I'll set that to 0 at the start of this function, just for safety.
422 BOOL BufferToStream
424 struct BufferList *buflist,
425 struct IFFHandle *iff,
426 struct IFFParseBase_intern * IFFParseBase
429 struct BufferNode *node,
430 *nextnode;
433 /* Number of bytes axtually writen to the stream,
434 or if negative: an IFFERR_.. error */
436 LONG byteswritten = 0,
437 bytes2write,
438 numbytes;
441 DEBUG_BUFFERTOSTREAM(dprintf("BufferToStream: buflist %p iff %p\n", buflist, iff));
443 numbytes = buflist->bl_BufferSize;
444 /* For safety. Read at the function header. */
445 GetIntIH(iff)->iff_BufferStartDepth = 0;
447 nextnode = (struct BufferNode*)buflist->bl_ListHeader.mlh_Head;
449 while (numbytes)
451 node = nextnode;
453 /* Should we write more than the first buffer ? */
455 if (numbytes > buflist->bl_BufferNodeSize)
458 nextnode = (struct BufferNode*)node->bn_Node.mln_Succ;
459 /* Check if there are enough buffers to write numbytesf */
460 if (!nextnode)
462 DEBUG_BUFFERTOSTREAM(dprintf("BufferToStream: return FALSE #1\n"));
463 return (FALSE);
467 bytes2write = buflist->bl_BufferNodeSize;
470 else
472 /* We are at the last buffer to write */
474 bytes2write = numbytes;
477 /* Write a buffernode to the stream */
478 byteswritten = WriteStream
480 iff,
481 node->bn_BufferPtr,
482 bytes2write,
483 IFFParseBase
486 /* Do we have a IFFERR_.. ? */
487 if (byteswritten < 0)
489 DEBUG_BUFFERTOSTREAM(dprintf("BufferToStream: return FALSE #2\n"));
490 return (FALSE);
494 /* There is bytes2write less to write */
496 numbytes -= bytes2write;
500 DEBUG_BUFFERTOSTREAM(dprintf("BufferToStream: return %ld\n", byteswritten));
501 return (byteswritten);
504 /*****************************************/
505 /* BufferStream initialization & cleanup */
506 /*****************************************/
508 /* Put in own functions just to make code tidier */
510 LONG InitBufferedStream(struct IFFHandle *iff,
511 struct IFFParseBase_intern * IFFParseBase)
513 DEBUG_INITBUFFEREDSTREAM(dprintf("InitBufferedStream: iff %p\n", iff));
517 !(GetIntIH(iff)->iff_BufferStartDepth)
520 /* Preserve the stream */
521 GetIntIH(iff)->iff_PreservedStream = iff->iff_Stream;
523 /* Allocate buffers that WriteStream can buffer its output to */
524 if (!( iff->iff_Stream = (IPTR)AllocBuffer(BUFFERNODESIZE, IFFParseBase)))
526 DEBUG_INITBUFFEREDSTREAM(dprintf("InitBufferedStream: return IFFERR_NOMEM\n"));
527 return (IFFERR_NOMEM);
530 /* To inform WriteStream that it should buffer all input */
531 /* + 1 because we have not PushContextNode()ed yet */
533 GetIntIH(iff)->iff_BufferStartDepth = iff->iff_Depth + 1;
535 /* Preserve the old hook and the old flags */
537 GetIntIH(iff)->iff_PreservedHandler = GetIntIH(iff)->iff_StreamHandler;
540 GetIntIH(iff)->iff_PreservedFlags = iff->iff_Flags;
543 /* Insert the BufStreamHandler into the hook instead */
544 InitIFF(iff, IFFF_RSEEK, &IFFParseBase->bufhook);
548 DEBUG_INITBUFFEREDSTREAM(dprintf("InitBufferedStream: return 0\n"));
549 return 0;
552 LONG ExitBufferedStream(struct IFFHandle *iff,
553 struct IFFParseBase_intern * IFFParseBase)
556 If we have come to the chunk that started internal buffering, then we should do the following.
558 - Turn it off, so that WriteStream again writes to the REAL stream.
559 - Write the whole buffer to this stream.
560 - Free the buffer/
562 LONG err = 0;
564 struct BufferList *buflist;
566 DEBUG_EXITBUFFEREDSTREAM(dprintf("ExitBufferedStream: iff %p\n", iff));
568 /* Turn off buffering */
569 GetIntIH(iff)->iff_BufferStartDepth = 0;
571 /* Read out the bufferlist stream pointer */
572 buflist = (struct BufferList*)iff->iff_Stream;
575 /* Now we can reinstall the old StreamHandler */
576 GetIntIH(iff)->iff_StreamHandler = GetIntIH(iff)->iff_PreservedHandler;
578 /* Reinstall the old flags */
579 iff->iff_Flags = GetIntIH(iff)->iff_PreservedFlags;
582 /* Reinstall the old stream */
583 iff->iff_Stream = GetIntIH(iff)->iff_PreservedStream;
586 /* Write all the buffers into the old stream */
587 if (!BufferToStream(buflist,
588 iff,
589 IFFParseBase))
591 err = IFFERR_WRITE;
594 FreeBuffer(buflist, IFFParseBase);
596 DEBUG_EXITBUFFEREDSTREAM(dprintf("ExitBufferedStream: return %ld\n", err));
597 return (err);
600 /**********************/
601 /* BufStreamHandler */
602 /**********************/
604 #define IFFParseBase (IPB(hook->h_Data))
606 ULONG BufStreamHandler
608 struct Hook *hook,
609 struct IFFHandle *iff,
610 struct IFFStreamCmd *cmd
614 ULONG error = 0;
616 DEBUG_BUFSTREAMHANDLER(dprintf("BufStreamHandler: hook %p iff %p cmd %p\n", hook, iff, cmd));
618 switch (cmd->sc_Command)
621 case IFFCMD_READ:
623 DEBUG_BUFSTREAMHANDLER(dprintf("BufStreamHandler: IFFCMD_READ...\n"));
625 /* It should NEVER be needed to read a buffered stream.
626 To output the buffered stream to the real stream,
627 we have the routne BufferToStream which is MUCH more effective
630 error = 1;
631 break;
633 case IFFCMD_WRITE:
635 DEBUG_BUFSTREAMHANDLER(dprintf("BufStreamHandler: IFFCMD_WRITE...\n"));
637 error =
639 WriteToBuffer
641 (struct BufferList*)iff->iff_Stream,
642 cmd->sc_Buf,
643 cmd->sc_NBytes,
644 IFFParseBase
647 cmd->sc_NBytes
650 break;
652 case IFFCMD_SEEK:
654 DEBUG_BUFSTREAMHANDLER(dprintf("BufStreamHandler: IFFCMD_SEEK...\n"));
656 error =
658 SeekBuffer
660 (struct BufferList*)iff->iff_Stream,
661 cmd->sc_NBytes
665 break;
667 case IFFCMD_INIT:
668 case IFFCMD_CLEANUP:
670 DEBUG_BUFSTREAMHANDLER(dprintf("BufStreamHandler: IFFCMD_INIT/IFFCMD_CLEANUP...\n"));
672 error = 0;
673 break;
677 DEBUG_BUFSTREAMHANDLER(dprintf("BufStreamHandler: return %ld\n", error));
678 return (error);