VirtualBox

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

Last change on this file since 2 was 2, checked in by bird, 17 years ago

Imported http://svn.netlabs.org/repos/libc/trunk/kStuff, revision 3612.

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