VirtualBox

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

Last change on this file since 79 was 79, checked in by bird, 8 years ago

fixing warnings.

  • Property svn:keywords set to Id Revision
File size: 22.8 KB
Line 
1/* $Id: kRdrBuffered.cpp 79 2016-07-27 14:25:09Z 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 K_NOREF(ppRdr);
380 K_NOREF(pszFilename);
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}
652
653
654/**
655 * Worker for kRdrBufLineQ that searches the current buffer for EOL or EOF.
656 *
657 * When a EOF marker is found
658 *
659 *
660 * @returns NULL if EOL/EOF isn't found the buffer.
661 * @param pThis The buffered reader instance.
662 */
663static const char * krdrBufLineQWorker(PKRDRBUF pThis)
664{
665 kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
666
667 /*
668 * Search the buffer.
669 */
670 kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
671 const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
672 const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid];
673 char *psz = (char *)pszStart;
674 while (psz < pszEnd)
675 {
676 char ch = *psz;
677 if (ch == '\n')
678 {
679 pThis->offFile += psz - pszStart;
680 pThis->fTainedByLineQ = K_TRUE;
681 *psz = '\0';
682 if ( psz > pszStart
683 && psz[-1] == '\r')
684 *--psz = '\0';
685 return pszStart;
686 }
687 psz++;
688 }
689
690 /*
691 * Check for EOF. There must be room for a terminator char here.
692 */
693 if ( pThis->offBufEnd >= pThis->cbFile
694 && (pThis->offBufEnd - pThis->offBuf) < (KSSIZE)pThis->cbBuf)
695 {
696 pThis->offFile = pThis->cbFile;
697 pThis->pbBuf[pThis->cbBufValid] = '\0';
698 return pszStart;
699 }
700
701 return NULL;
702}
703
704
705/**
706 * Get the pointer to the next next line in the buffer.
707 * The returned line is zero terminated.
708 *
709 * @returns A pointer to the line on success. This becomes invalid
710 * upon the next call to this kRdr instance.
711 * @returns NULL on EOF, read error of if the line was too long.
712 * @param pRdr The buffered file reader.
713 */
714KRDR_DECL(const char *) kRdrBufLineQ(PKRDR pRdr)
715{
716 /*
717 * Validate input.
718 */
719 KRDR_VALIDATE_EX(pRdr, NULL);
720 kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, NULL);
721
722 /* check for EOF */
723 PKRDRBUF pThis = (PKRDRBUF)pRdr;
724 if (pThis->offFile >= pThis->cbFile)
725 {
726 kRdrAssert(pThis->offFile == pThis->cbFile);
727 return NULL;
728 }
729
730 /*
731 * Search the current buffer if possible
732 */
733 if ( pThis->cbBufValid
734 && pThis->offFile >= pThis->offBuf
735 && pThis->offFile < pThis->offBufEnd)
736 {
737 const char *psz = krdrBufLineQWorker(pThis);
738 if (psz)
739 return psz;
740 }
741
742 /*
743 * Fill the buffer in an optimal way and look for the EOL/EOF (again).
744 */
745 int rc = krdrBufFillBuffer(pThis, pThis->offFile);
746 if (rc)
747 return NULL;
748 return krdrBufLineQWorker(pThis);
749}
750
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