VirtualBox

source: kStuff/trunk/kRdr/kRdrBuffered.cpp@ 52

Last change on this file since 52 was 29, checked in by bird, 15 years ago

Finally got around execute the switch to the MIT license.

  • Property svn:keywords set to Id Revision
File size: 22.7 KB
Line 
1/* $Id: kRdrBuffered.cpp 29 2009-07-01 20:30:29Z bird $ */
2/** @file
3 * kRdrBuffered - Buffered File Provider.
4 */
5
6/*
7 * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include "kRdrInternal.h"
35#include <k/kHlpAlloc.h>
36#include <k/kHlpString.h>
37
38
39/*******************************************************************************
40* Structures and Typedefs *
41*******************************************************************************/
42/**
43 * The buffered file provier instance.
44 * This is just a wrapper around another file provider.
45 */
46typedef struct KRDRBUF
47{
48 /** The file reader vtable. */
49 KRDR Core;
50 /** The actual file provider that we're wrapping. */
51 PKRDR pRdr;
52 /** The current file offset. */
53 KFOFF offFile;
54 /** The file size. */
55 KFOFF cbFile;
56 /** The offset of the buffer. */
57 KFOFF offBuf;
58 /** The offset of the end of the buffer. */
59 KFOFF offBufEnd;
60 /** The number of valid buffer bytes. */
61 KSIZE cbBufValid;
62 /** The size of the buffer. */
63 KSIZE cbBuf;
64 /** The buffer. */
65 KU8 *pbBuf;
66 /** Whether the pRdr instance should be closed together with us or not. */
67 KBOOL fCloseIt;
68 /** Set if the buffer has been messed up by kRdrBufLineQ. */
69 KBOOL fTainedByLineQ;
70} KRDRBUF, *PKRDRBUF;
71
72
73/*******************************************************************************
74* Internal Functions *
75*******************************************************************************/
76static void krdrBufDone(PKRDR pRdr);
77static int krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
78static int krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
79static int krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
80static int krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
81static KSIZE krdrBufPageSize(PKRDR pRdr);
82static const char *krdrBufName(PKRDR pRdr);
83static KIPTR krdrBufNativeFH(PKRDR pRdr);
84static KFOFF krdrBufTell(PKRDR pRdr);
85static KFOFF krdrBufSize(PKRDR pRdr);
86static int krdrBufAllUnmap(PKRDR pRdr, const void *pvBits);
87static int krdrBufAllMap(PKRDR pRdr, const void **ppvBits);
88static int krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off);
89static int krdrBufDestroy(PKRDR pRdr);
90static int krdrBufCreate(PPKRDR ppRdr, const char *pszFilename);
91
92
93/*******************************************************************************
94* Global Variables *
95*******************************************************************************/
96/** Native file provider operations.
97 *
98 * @remark This is not in the file provider list as its intended for wrapping
99 * other kRdr instances.
100 */
101static const KRDROPS g_krdrBufOps =
102{
103 "Buffered kRdr",
104 NULL,
105 krdrBufCreate,
106 krdrBufDestroy,
107 krdrBufRead,
108 krdrBufAllMap,
109 krdrBufAllUnmap,
110 krdrBufSize,
111 krdrBufTell,
112 krdrBufName,
113 krdrBufNativeFH,
114 krdrBufPageSize,
115 krdrBufMap,
116 krdrBufRefresh,
117 krdrBufProtect,
118 krdrBufUnmap,
119 krdrBufDone,
120 42
121};
122
123
124/** @copydoc KRDROPS::pfnDone */
125static void krdrBufDone(PKRDR pRdr)
126{
127 PKRDRBUF pThis = (PKRDRBUF)pRdr;
128 return pThis->pRdr->pOps->pfnDone(pThis->pRdr);
129}
130
131
132/** @copydoc KRDROPS::pfnUnmap */
133static int krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
134{
135 PKRDRBUF pThis = (PKRDRBUF)pRdr;
136 return pThis->pRdr->pOps->pfnUnmap(pThis->pRdr, pvBase, cSegments, paSegments);
137}
138
139
140/** @copydoc KRDROPS::pfnProtect */
141static int krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
142{
143 PKRDRBUF pThis = (PKRDRBUF)pRdr;
144 return pThis->pRdr->pOps->pfnProtect(pThis->pRdr, pvBase, cSegments, paSegments, fUnprotectOrProtect);
145}
146
147
148/** @copydoc KRDROPS::pfnRefresh */
149static int krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
150{
151 PKRDRBUF pThis = (PKRDRBUF)pRdr;
152 return pThis->pRdr->pOps->pfnRefresh(pThis->pRdr, pvBase, cSegments, paSegments);
153}
154
155
156/** @copydoc KRDROPS::pfnMap */
157static int krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
158{
159 PKRDRBUF pThis = (PKRDRBUF)pRdr;
160 return pThis->pRdr->pOps->pfnMap(pThis->pRdr, ppvBase, cSegments, paSegments, fFixed);
161}
162
163
164/** @copydoc KRDROPS::pfnPageSize */
165static KSIZE krdrBufPageSize(PKRDR pRdr)
166{
167 PKRDRBUF pThis = (PKRDRBUF)pRdr;
168 return pThis->pRdr->pOps->pfnPageSize(pThis->pRdr);
169}
170
171
172/** @copydoc KRDROPS::pfnName */
173static const char *krdrBufName(PKRDR pRdr)
174{
175 PKRDRBUF pThis = (PKRDRBUF)pRdr;
176 return pThis->pRdr->pOps->pfnName(pThis->pRdr);
177}
178
179
180/** @copydoc KRDROPS::pfnNativeFH */
181static KIPTR krdrBufNativeFH(PKRDR pRdr)
182{
183 PKRDRBUF pThis = (PKRDRBUF)pRdr;
184 return pThis->pRdr->pOps->pfnNativeFH(pThis->pRdr);
185}
186
187
188/** @copydoc KRDROPS::pfnTell */
189static KFOFF krdrBufTell(PKRDR pRdr)
190{
191 PKRDRBUF pThis = (PKRDRBUF)pRdr;
192 return pThis->offFile;
193}
194
195
196/** @copydoc KRDROPS::pfnSize */
197static KFOFF krdrBufSize(PKRDR pRdr)
198{
199 PKRDRBUF pThis = (PKRDRBUF)pRdr;
200 return pThis->cbFile;
201}
202
203
204/** @copydoc KRDROPS::pfnAllUnmap */
205static int krdrBufAllUnmap(PKRDR pRdr, const void *pvBits)
206{
207 PKRDRBUF pThis = (PKRDRBUF)pRdr;
208 return pThis->pRdr->pOps->pfnAllUnmap(pThis->pRdr, pvBits);
209}
210
211
212/** @copydoc KRDROPS::pfnAllMap */
213static int krdrBufAllMap(PKRDR pRdr, const void **ppvBits)
214{
215 PKRDRBUF pThis = (PKRDRBUF)pRdr;
216 return pThis->pRdr->pOps->pfnAllMap(pThis->pRdr, ppvBits);
217}
218
219
220/**
221 * Fills the buffer with file bits starting at the specified offset.
222 *
223 * @returns 0 on success, pfnRead error code on failure.
224 * @param pThis The instance.
225 * @param off Where to start reading.
226 */
227static int krdrBufFillBuffer(PKRDRBUF pThis, KFOFF off)
228{
229 kRdrAssert(off < pThis->cbFile);
230
231 /* Reposition the buffer if it's past the end of the file so that
232 we maximize its usability. We leave one unused byte at the end
233 of the buffer so kRdrBufLineQ can terminate its string properly.
234 Of course, this might end up re-reading a lot of stuff for no
235 future gain, but whatever... */
236 kRdrAssert(pThis->cbBuf <= pThis->cbFile + 1);
237 KFOFF cbLeft = pThis->cbFile - off;
238 KSIZE cbRead = pThis->cbBuf;
239 if ((KSSIZE)cbRead - 1 >= cbLeft)
240 {
241 cbRead--;
242 off = pThis->cbFile - cbRead;
243 }
244 int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pThis->pbBuf, cbRead, off);
245 if (!rc)
246 {
247 pThis->offBuf = off;
248 pThis->offBufEnd = off + cbRead;
249 pThis->cbBufValid = cbRead;
250 }
251 else
252 {
253 pThis->offBuf = pThis->offBufEnd = 0;
254 pThis->cbBufValid = 0;
255 }
256 pThis->fTainedByLineQ = K_FALSE;
257 return rc;
258}
259
260
261/** @copydoc KRDROPS::pfnRead */
262static int krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
263{
264 PKRDRBUF pThis = (PKRDRBUF)pRdr;
265
266 /*
267 * We need to validate and update the file offset before
268 * we start making partial reads from the buffer and stuff.
269 */
270 KFOFF offEnd = off + cb;
271 if ( off >= pThis->cbFile
272 || offEnd > pThis->cbFile
273 || offEnd < off)
274 return KERR_OUT_OF_RANGE; /* includes EOF. */
275 pThis->offFile = offEnd;
276 if (!cb)
277 return 0;
278
279 /*
280 * Scratch the buffer if kRdrBufLineQ has tained it.
281 */
282 if (pThis->fTainedByLineQ)
283 {
284 pThis->offBuf = pThis->offBufEnd = 0;
285 pThis->cbBufValid = 0;
286 }
287
288 /*
289 * Is any part of the request in the buffer?
290 *
291 * We will currently ignore buffer hits in the middle of the
292 * request because it's annoying to implement and it's
293 * questionable whether it'll benefit much performance wise.
294 */
295 if (pThis->cbBufValid > 0)
296 {
297 if (off >= pThis->offBuf)
298 {
299 if (off < pThis->offBufEnd)
300 {
301 /* head (or all) of the request is in the buffer. */
302 KSIZE cbMaxChunk = (KSIZE)(pThis->offBufEnd - off);
303 KSIZE cbChunk = K_MIN(cb, cbMaxChunk);
304 kHlpMemCopy(pvBuf, &pThis->pbBuf[off - pThis->offBuf], cbChunk);
305 if (cbChunk == cb)
306 return 0;
307
308 cb -= cbChunk;
309 pvBuf = (KU8 *)pvBuf + cbChunk;
310 off += cbChunk;
311 }
312 }
313 else if ( offEnd > pThis->offBuf
314 && offEnd <= pThis->offBufEnd)
315 {
316 /* the end of the request is in the buffer. */
317 KSIZE cbChunk = (KSIZE)(pThis->offBufEnd - (offEnd));
318 kHlpMemCopy((KU8 *)pvBuf + (pThis->offBuf - off), pThis->pbBuf, cbChunk);
319 kRdrAssert(cbChunk < cb);
320 cb -= cbChunk;
321 offEnd -= cbChunk;
322 }
323 }
324
325 /*
326 * If the buffer is larger than the read request, read a full buffer
327 * starting at the requested offset. Otherwise perform an unbuffered
328 * read.
329 */
330 if (pThis->cbBuf > cb)
331 {
332 int rc = krdrBufFillBuffer(pThis, off);
333 if (rc)
334 return rc;
335 if (pThis->offBuf == off)
336 kHlpMemCopy(pvBuf, pThis->pbBuf, cb);
337 else
338 {
339 kRdrAssert(off > pThis->offBuf);
340 kRdrAssert(off + cb <= pThis->offBufEnd);
341 kHlpMemCopy(pvBuf, pThis->pbBuf + (off - pThis->offBuf), cb);
342 }
343 }
344 else
345 {
346 int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pvBuf, cb, off);
347 if (rc)
348 return rc;
349 }
350 return 0;
351}
352
353
354/** @copydoc KRDROPS::pfnDestroy */
355static int krdrBufDestroy(PKRDR pRdr)
356{
357 PKRDRBUF pThis = (PKRDRBUF)pRdr;
358
359 /* Close the kRdr instance that we're wrapping. */
360 if (pThis->fCloseIt)
361 {
362 int rc = pThis->pRdr->pOps->pfnDestroy(pThis->pRdr);
363 if (rc)
364 return rc;
365 pThis->fCloseIt = K_FALSE;
366 pThis->pRdr = NULL;
367 }
368
369 kHlpFree(pThis->pbBuf);
370 pThis->pbBuf = NULL;
371 kHlpFree(pRdr);
372 return 0;
373}
374
375
376/** @copydoc KRDROPS::pfnCreate */
377static int krdrBufCreate(PPKRDR ppRdr, const char *pszFilename)
378{
379 return KERR_NOT_IMPLEMENTED;
380}
381
382
383/**
384 * Worker for kRdrBufOpen and kRdrBufWrap.
385 *
386 * It's essentially kRdrBufWrap without error checking.
387 *
388 * @returns 0 on success, one of the kErrors status code on failure.
389 * @param ppRdr Where to store the new file provider instance.
390 * @param pRdrWrapped The file provider instance to buffer.
391 * @param fCloseIt Whether it the pRdrWrapped instance should be closed
392 * when the new instance is closed.
393 */
394static int krdrBufWrapIt(PPKRDR ppRdr, PKRDR pRdrWrapped, KBOOL fCloseIt)
395{
396 PKRDRBUF pThis = (PKRDRBUF)kHlpAlloc(sizeof(*pThis));
397 if (pThis)
398 {
399 pThis->Core.u32Magic = KRDR_MAGIC;
400 pThis->Core.pOps = &g_krdrBufOps;
401 pThis->pRdr = pRdrWrapped;
402 pThis->offFile = pRdrWrapped->pOps->pfnTell(pRdrWrapped);
403 pThis->cbFile = pRdrWrapped->pOps->pfnSize(pRdrWrapped);
404 pThis->offBuf = pThis->offBufEnd = 0;
405 pThis->cbBufValid = 0;
406 pThis->fCloseIt = fCloseIt;
407 pThis->fTainedByLineQ = K_FALSE;
408 if (pThis->cbFile < 128*1024)
409 pThis->cbBuf = (KSIZE)pThis->cbFile + 1; /* need space for the kRdrBufLineQ terminator. */
410 else
411 pThis->cbBuf = 64*1024;
412 pThis->pbBuf = (KU8 *)kHlpAlloc(pThis->cbBuf);
413 if (pThis->pbBuf)
414 {
415 *ppRdr = &pThis->Core;
416 return 0;
417 }
418
419 pThis->Core.u32Magic = 0;
420 kHlpFree(pThis);
421 }
422 return KERR_NO_MEMORY;
423}
424
425
426/**
427 * Opens a file provider with a buffered wrapper.
428 *
429 * @returns 0 on success, KERR_* on failure.
430 * @param ppRdr Where to store the buffered file reader instance on success.
431 * @param pszFilename The name of the file that should be opened.
432 */
433KRDR_DECL(int) kRdrBufOpen(PPKRDR ppRdr, const char *pszFilename)
434{
435 kRdrAssertPtrReturn(ppRdr, KERR_INVALID_POINTER);
436 *ppRdr = NULL;
437
438 PKRDR pRdrWrapped;
439 int rc = kRdrOpen(&pRdrWrapped, pszFilename);
440 if (!rc)
441 {
442 rc = krdrBufWrapIt(ppRdr, pRdrWrapped, K_TRUE);
443 if (rc)
444 kRdrClose(pRdrWrapped);
445 }
446 return rc;
447}
448
449
450/**
451 * Creates a buffered file provider instance for an existing one.
452 *
453 * @returns 0 on success, KERR_* on failure.
454 * @param ppRdr Where to store the new file provider pointer.
455 * @param pRdr The file provider instance to wrap.
456 * @param fCLoseIt Whether it the wrapped reader should be automatically
457 * closed when the wrapper closes.
458 */
459KRDR_DECL(int) kRdrBufWrap(PPKRDR ppRdr, PKRDR pRdr, KBOOL fCloseIt)
460{
461 KRDR_VALIDATE(pRdr);
462 return krdrBufWrapIt(ppRdr, pRdr, fCloseIt);
463}
464
465
466/**
467 * Checks whether the file provider instance is of the buffered type or not.
468 *
469 * @returns K_TRUE if it is, otherwise K_FALSE.
470 * @param pRdr The file provider instance to check.
471 */
472KRDR_DECL(KBOOL) kRdrBufIsBuffered(PKRDR pRdr)
473{
474 KRDR_VALIDATE_EX(pRdr, K_FALSE);
475 return pRdr->pOps == &g_krdrBufOps;
476}
477
478
479/**
480 * Reads a line from a buffered file provider.
481 *
482 * The trailing '\n' or '\r\n' is stripped.
483 *
484 * @returns 0 on success. KERR_* on failure.
485 * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
486 * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
487 * @param pRdr The buffered file reader.
488 * @param pszLine Where to store the line.
489 * @param cbLine The size of the the line buffer.
490 */
491KRDR_DECL(int) kRdrBufLine(PKRDR pRdr, char *pszLine, KSIZE cbLine)
492{
493 return kRdrBufLineEx(pRdr, pszLine, &cbLine);
494}
495
496
497/**
498 * Reads a line from a buffered file provider.
499 *
500 * The trailing '\n' or '\r\n' is stripped.
501 *
502 * @returns 0 on success. KERR_* on failure.
503 * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
504 * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
505 * @param pRdr The buffered file reader.
506 * @param pszLine Where to store the line.
507 * @param pcbLine The size of the the line buffer on input, the length of the
508 * returned line on output.
509 */
510KRDR_DECL(int) kRdrBufLineEx(PKRDR pRdr, char *pszLine, KSIZE *pcbLine)
511{
512 /*
513 * Validate input.
514 */
515 kRdrAssertPtrReturn(pcbLine, KERR_INVALID_POINTER);
516 KSIZE cbLeft = *pcbLine;
517 *pcbLine = 0;
518 kRdrAssertReturn(cbLeft > 0, KERR_INVALID_PARAMETER);
519 KRDR_VALIDATE(pRdr);
520 kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, KRDR_ERR_NOT_BUFFERED_RDR);
521 kRdrAssertPtrReturn(pszLine, KERR_INVALID_POINTER);
522
523 /* check for EOF */
524 PKRDRBUF pThis = (PKRDRBUF)pRdr;
525 if (pThis->offFile >= pThis->cbFile)
526 {
527 kRdrAssert(pThis->offFile == pThis->cbFile);
528 *pszLine = '\0';
529 *pcbLine = 0;
530 return KERR_EOF;
531 }
532
533 /*
534 * Scratch the buffer if kRdrBufLineQ has tained it.
535 */
536 if (pThis->fTainedByLineQ)
537 {
538 pThis->offBuf = pThis->offBufEnd = 0;
539 pThis->cbBufValid = 0;
540 }
541
542 /*
543 * Buffered read loop.
544 *
545 * The overflow logic is a bit fishy wrt to overflowing at an "\r\n"
546 * that arrives at a buffer boundrary. The current policy is to try
547 * our best to not to fail with overflow in the EOL sequence or EOF.
548 * If it's the end of the buffer, it will not be refilled just to
549 * check for this because that's too much work.
550 */
551 cbLeft--; /* reserve space for the terminator. */
552 char *pszOut = pszLine;
553 for (;;)
554 {
555 /*
556 * Do we need to (re-)fill the buffer or does it contain something
557 * that we can work on already?
558 */
559 if ( !pThis->cbBufValid
560 || pThis->offFile >= pThis->offBufEnd
561 || pThis->offFile < pThis->offBuf)
562 {
563 int rc = krdrBufFillBuffer(pThis, pThis->offFile);
564 if (rc)
565 {
566 *pszOut = '\0';
567 return rc;
568 }
569 }
570
571 /*
572 * Parse the buffer looking for the EOL indicator.
573 */
574 kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
575 kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
576 const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
577 const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid];
578 const char *psz = pszStart;
579 while (psz < pszEnd)
580 {
581 const char ch = *psz;
582 if (ch == '\n')
583 {
584 /* found the EOL, update file position and line length. */
585 pThis->offFile += psz - pszStart + 1;
586 *pcbLine += psz - pszStart;
587
588 /* terminate the string, checking for "\r\n" first. */
589 if ( *pcbLine
590 && pszOut[-1] == '\r')
591 {
592 *pcbLine -= 1;
593 pszOut--;
594 }
595 *pszOut = '\0';
596 return 0;
597 }
598 if (!cbLeft)
599 {
600 /* the line is *probably* too long. */
601 pThis->offFile += psz - pszStart;
602 *pcbLine += psz - pszStart;
603 *pszOut = '\0';
604
605 /* The only possible case where the line actually isn't too long
606 is if we're at a "\r\n" sequence. We will re-fill the buffer
607 if necessary to check for the '\n' as it's not that much work. */
608 if ( ch == '\r'
609 && pThis->offFile + 2 <= pThis->cbFile)
610 {
611 if (psz + 1 >= pszEnd)
612 {
613 int rc = krdrBufFillBuffer(pThis, pThis->offFile);
614 if (rc)
615 {
616 *pszOut = '\0';
617 return rc;
618 }
619 }
620 psz = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
621 kRdrAssert(*psz == '\r');
622 if (psz[1] == '\n')
623 {
624 *pcbLine -= 1;
625 pszOut[-1] = '\0';
626 pThis->offFile += 2;
627 return 0;
628 }
629 }
630 return KRDR_ERR_LINE_TOO_LONG;
631 }
632
633 /* copy and advance */
634 *pszOut++ = ch;
635 cbLeft--;
636 psz++;
637 }
638
639 /* advance past the buffer and check for EOF. */
640 *pcbLine += pszEnd - pszStart;
641 pThis->offFile = pThis->offBufEnd;
642 if (pThis->offFile >= pThis->cbFile)
643 {
644 kRdrAssert(pThis->offFile == pThis->cbFile);
645 *pszOut = '\0';
646 return 0;
647 }
648 }
649 return -1;
650}
651
652
653/**
654 * Worker for kRdrBufLineQ that searches the current buffer for EOL or EOF.
655 *
656 * When a EOF marker is found
657 *
658 *
659 * @returns NULL if EOL/EOF isn't found the buffer.
660 * @param pThis The buffered reader instance.
661 */
662static const char * krdrBufLineQWorker(PKRDRBUF pThis)
663{
664 kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
665
666 /*
667 * Search the buffer.
668 */
669 kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
670 const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
671 const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid];
672 char *psz = (char *)pszStart;
673 while (psz < pszEnd)
674 {
675 char ch = *psz;
676 if (ch == '\n')
677 {
678 pThis->offFile += psz - pszStart;
679 pThis->fTainedByLineQ = K_TRUE;
680 *psz = '\0';
681 if ( psz > pszStart
682 && psz[-1] == '\r')
683 *--psz = '\0';
684 return pszStart;
685 }
686 psz++;
687 }
688
689 /*
690 * Check for EOF. There must be room for a terminator char here.
691 */
692 if ( pThis->offBufEnd >= pThis->cbFile
693 && (pThis->offBufEnd - pThis->offBuf) < (KSSIZE)pThis->cbBuf)
694 {
695 pThis->offFile = pThis->cbFile;
696 pThis->pbBuf[pThis->cbBufValid] = '\0';
697 return pszStart;
698 }
699
700 return NULL;
701}
702
703
704/**
705 * Get the pointer to the next next line in the buffer.
706 * The returned line is zero terminated.
707 *
708 * @returns A pointer to the line on success. This becomes invalid
709 * upon the next call to this kRdr instance.
710 * @returns NULL on EOF, read error of if the line was too long.
711 * @param pRdr The buffered file reader.
712 */
713KRDR_DECL(const char *) kRdrBufLineQ(PKRDR pRdr)
714{
715 /*
716 * Validate input.
717 */
718 KRDR_VALIDATE_EX(pRdr, NULL);
719 kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, NULL);
720
721 /* check for EOF */
722 PKRDRBUF pThis = (PKRDRBUF)pRdr;
723 if (pThis->offFile >= pThis->cbFile)
724 {
725 kRdrAssert(pThis->offFile == pThis->cbFile);
726 return NULL;
727 }
728
729 /*
730 * Search the current buffer if possible
731 */
732 if ( pThis->cbBufValid
733 && pThis->offFile >= pThis->offBuf
734 && pThis->offFile < pThis->offBufEnd)
735 {
736 const char *psz = krdrBufLineQWorker(pThis);
737 if (psz)
738 return psz;
739 }
740
741 /*
742 * Fill the buffer in an optimal way and look for the EOL/EOF (again).
743 */
744 int rc = krdrBufFillBuffer(pThis, pThis->offFile);
745 if (rc)
746 return NULL;
747 return krdrBufLineQWorker(pThis);
748}
749
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette