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

Last change on this file since 3548 was 3548, checked in by bird, 18 years ago

kRdrBuf - a buffered kRdr wrapper.

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