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

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

kDbgDump can open itself now, not bad.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 22.4 KB
Line 
1/* $Id: kRdrBuffered.cpp 3552 2007-08-26 02:15:22Z 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 /** Whether the pRdr instance should be closed together with us or not. */
63 KBOOL fCloseIt;
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->fCloseIt)
357 {
358 int rc = pThis->pRdr->pOps->pfnDestroy(pThis->pRdr);
359 if (rc)
360 return rc;
361 pThis->fCloseIt = 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 error checking.
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 fCloseIt Whether it the pRdrWrapped instance should be closed
388 * when the new instance is closed.
389 */
390static int krdrBufWrapIt(PPKRDR ppRdr, PKRDR pRdrWrapped, KBOOL fCloseIt)
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->fCloseIt = fCloseIt;
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 return KERR_NO_MEMORY;
419}
420
421
422/**
423 * Opens a file provider with a buffered wrapper.
424 *
425 * @returns 0 on success, KERR_* on failure.
426 * @param ppRdr Where to store the buffered file reader instance on success.
427 * @param pszFilename The name of the file that should be opened.
428 */
429KRDR_DECL(int) kRdrBufOpen(PPKRDR ppRdr, const char *pszFilename)
430{
431 kRdrAssertPtrReturn(ppRdr, KERR_INVALID_POINTER);
432 *ppRdr = NULL;
433
434 PKRDR pRdrWrapped;
435 int rc = kRdrOpen(&pRdrWrapped, pszFilename);
436 if (!rc)
437 {
438 rc = krdrBufWrapIt(ppRdr, pRdrWrapped, K_TRUE);
439 if (rc)
440 kRdrClose(pRdrWrapped);
441 }
442 return rc;
443}
444
445
446/**
447 * Creates a buffered file provider instance for an existing one.
448 *
449 * @returns 0 on success, KERR_* on failure.
450 * @param ppRdr Where to store the new file provider pointer.
451 * @param pRdr The file provider instance to wrap.
452 * @param fCLoseIt Whether it the wrapped reader should be automatically
453 * closed when the wrapper closes.
454 */
455KRDR_DECL(int) kRdrBufWrap(PPKRDR ppRdr, PKRDR pRdr, KBOOL fCloseIt)
456{
457 KRDR_VALIDATE(pRdr);
458 return krdrBufWrapIt(ppRdr, pRdr, fCloseIt);
459}
460
461
462/**
463 * Checks whether the file provider instance is of the buffered type or not.
464 *
465 * @returns K_TRUE if it is, otherwise K_FALSE.
466 * @param pRdr The file provider instance to check.
467 */
468KRDR_DECL(KBOOL) kRdrBufIsBuffered(PKRDR pRdr)
469{
470 KRDR_VALIDATE_EX(pRdr, K_FALSE);
471 return pRdr->pOps == &g_krdrBufOps;
472}
473
474
475/**
476 * Reads a line from a buffered file provider.
477 *
478 * The trailing '\n' or '\r\n' is stripped.
479 *
480 * @returns 0 on success. KERR_* on failure.
481 * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
482 * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
483 * @param pRdr The buffered file reader.
484 * @param pszLine Where to store the line.
485 * @param cbLine The size of the the line buffer.
486 */
487KRDR_DECL(int) kRdrBufLine(PKRDR pRdr, char *pszLine, KSIZE cbLine)
488{
489 return kRdrBufLineEx(pRdr, pszLine, &cbLine);
490}
491
492
493/**
494 * Reads a line from a buffered file provider.
495 *
496 * The trailing '\n' or '\r\n' is stripped.
497 *
498 * @returns 0 on success. KERR_* on failure.
499 * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
500 * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
501 * @param pRdr The buffered file reader.
502 * @param pszLine Where to store the line.
503 * @param pcbLine The size of the the line buffer on input, the length of the
504 * returned line on output.
505 */
506KRDR_DECL(int) kRdrBufLineEx(PKRDR pRdr, char *pszLine, KSIZE *pcbLine)
507{
508 /*
509 * Validate input.
510 */
511 kRdrAssertPtrReturn(pcbLine, KERR_INVALID_POINTER);
512 KSIZE cbLeft = *pcbLine;
513 *pcbLine = 0;
514 kRdrAssertReturn(cbLeft > 0, KERR_INVALID_PARAMETER);
515 KRDR_VALIDATE(pRdr);
516 kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, KRDR_ERR_NOT_BUFFERED_RDR);
517 kRdrAssertPtrReturn(pszLine, KERR_INVALID_POINTER);
518
519 /* check for EOF */
520 PKRDRBUF pThis = (PKRDRBUF)pRdr;
521 if (pThis->offFile >= pThis->cbFile)
522 {
523 kRdrAssert(pThis->offFile == pThis->cbFile);
524 *pszLine = '\0';
525 *pcbLine = 0;
526 return KERR_EOF;
527 }
528
529 /*
530 * Scratch the buffer if kRdrBufLineQ has tained it.
531 */
532 if (pThis->fTainedByLineQ)
533 {
534 pThis->offBuf = pThis->offBufEnd = 0;
535 pThis->cbBufValid = 0;
536 }
537
538 /*
539 * Buffered read loop.
540 *
541 * The overflow logic is a bit fishy wrt to overflowing at an "\r\n"
542 * that arrives at a buffer boundrary. The current policy is to try
543 * our best to not to fail with overflow in the EOL sequence or EOF.
544 * If it's the end of the buffer, it will not be refilled just to
545 * check for this because that's too much work.
546 */
547 cbLeft--; /* reserve space for the terminator. */
548 char *pszOut = pszLine;
549 for (;;)
550 {
551 /*
552 * Do we need to (re-)fill the buffer or does it contain something
553 * that we can work on already?
554 */
555 if ( !pThis->cbBufValid
556 || pThis->offFile >= pThis->offBufEnd
557 || pThis->offFile < pThis->offBuf)
558 {
559 int rc = krdrBufFillBuffer(pThis, pThis->offFile);
560 if (rc)
561 {
562 *pszOut = '\0';
563 return rc;
564 }
565 }
566
567 /*
568 * Parse the buffer looking for the EOL indicator.
569 */
570 kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
571 kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
572 const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
573 const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid];
574 const char *psz = pszStart;
575 while (psz < pszEnd)
576 {
577 const char ch = *psz;
578 if (ch == '\n')
579 {
580 /* found the EOL, update file position and line length. */
581 pThis->offFile += psz - pszStart + 1;
582 *pcbLine += psz - pszStart;
583
584 /* terminate the string, checking for "\r\n" first. */
585 if ( *pcbLine
586 && pszOut[-1] == '\r')
587 {
588 *pcbLine -= 1;
589 pszOut--;
590 }
591 *pszOut = '\0';
592 return 0;
593 }
594 if (!cbLeft)
595 {
596 /* the line is *probably* too long. */
597 pThis->offFile += psz - pszStart;
598 *pcbLine += psz - pszStart;
599 *pszOut = '\0';
600
601 /* The only possible case where the line actually isn't too long
602 is if we're at a "\r\n" sequence. We will re-fill the buffer
603 if necessary to check for the '\n' as it's not that much work. */
604 if ( ch == '\r'
605 && pThis->offFile + 2 <= pThis->cbFile)
606 {
607 if (psz + 1 >= pszEnd)
608 {
609 int rc = krdrBufFillBuffer(pThis, pThis->offFile);
610 if (rc)
611 {
612 *pszOut = '\0';
613 return rc;
614 }
615 }
616 psz = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
617 kRdrAssert(*psz == '\r');
618 if (psz[1] == '\n')
619 {
620 *pcbLine -= 1;
621 pszOut[-1] = '\0';
622 pThis->offFile += 2;
623 return 0;
624 }
625 }
626 return KRDR_ERR_LINE_TOO_LONG;
627 }
628
629 /* copy and advance */
630 *pszOut++ = ch;
631 cbLeft--;
632 psz++;
633 }
634
635 /* advance past the buffer and check for EOF. */
636 *pcbLine += pszEnd - pszStart;
637 pThis->offFile = pThis->offBufEnd;
638 if (pThis->offFile >= pThis->cbFile)
639 {
640 kRdrAssert(pThis->offFile == pThis->cbFile);
641 *pszOut = '\0';
642 return 0;
643 }
644 }
645 return -1;
646}
647
648
649/**
650 * Worker for kRdrBufLineQ that searches the current buffer for EOL or EOF.
651 *
652 * When a EOF marker is found
653 *
654 *
655 * @returns NULL if EOL/EOF isn't found the buffer.
656 * @param pThis The buffered reader instance.
657 */
658static const char * krdrBufLineQWorker(PKRDRBUF pThis)
659{
660 kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
661
662 /*
663 * Search the buffer.
664 */
665 kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
666 const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
667 const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid];
668 char *psz = (char *)pszStart;
669 while (psz < pszEnd)
670 {
671 char ch = *psz;
672 if (ch == '\n')
673 {
674 pThis->offFile += psz - pszStart;
675 pThis->fTainedByLineQ = K_TRUE;
676 *psz = '\0';
677 if ( psz > pszStart
678 && psz[-1] == '\r')
679 *--psz = '\0';
680 return pszStart;
681 }
682 psz++;
683 }
684
685 /*
686 * Check for EOF. There must be room for a terminator char here.
687 */
688 if ( pThis->offBufEnd >= pThis->cbFile
689 && (pThis->offBufEnd - pThis->offBuf) < pThis->cbBuf)
690 {
691 pThis->offFile = pThis->cbFile;
692 pThis->pbBuf[pThis->cbBufValid] = '\0';
693 return pszStart;
694 }
695
696 return NULL;
697}
698
699
700/**
701 * Get the pointer to the next next line in the buffer.
702 * The returned line is zero terminated.
703 *
704 * @returns A pointer to the line on success. This becomes invalid
705 * upon the next call to this kRdr instance.
706 * @returns NULL on EOF, read error of if the line was too long.
707 * @param pRdr The buffered file reader.
708 */
709KRDR_DECL(const char *) kRdrBufLineQ(PKRDR pRdr)
710{
711 /*
712 * Validate input.
713 */
714 KRDR_VALIDATE_EX(pRdr, NULL);
715 kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, NULL);
716
717 /* check for EOF */
718 PKRDRBUF pThis = (PKRDRBUF)pRdr;
719 if (pThis->offFile >= pThis->cbFile)
720 {
721 kRdrAssert(pThis->offFile == pThis->cbFile);
722 return NULL;
723 }
724
725 /*
726 * Search the current buffer if possible
727 */
728 if ( pThis->cbBufValid
729 && pThis->offFile >= pThis->offBuf
730 && pThis->offFile < pThis->offBufEnd)
731 {
732 const char *psz = krdrBufLineQWorker(pThis);
733 if (psz)
734 return psz;
735 }
736
737 /*
738 * Fill the buffer in an optimal way and look for the EOL/EOF (again).
739 */
740 int rc = krdrBufFillBuffer(pThis, pThis->offFile);
741 if (rc)
742 return NULL;
743 return krdrBufLineQWorker(pThis);
744}
745
Note: See TracBrowser for help on using the repository browser.