Guillaume Chatelet | 3cc8f31 | 2020-10-12 08:55:20 +0000 | [diff] [blame] | 1 | // Copyright 2017 Google LLC |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "internal/stack_line_reader.h" |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 16 | |
| 17 | #include <assert.h> |
| 18 | #include <errno.h> |
| 19 | #include <stdio.h> |
| 20 | |
Guillaume Chatelet | 22a5362 | 2020-09-23 11:52:20 +0200 | [diff] [blame] | 21 | #include "internal/filesystem.h" |
| 22 | |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 23 | void StackLineReader_Initialize(StackLineReader* reader, int fd) { |
| 24 | reader->view.ptr = reader->buffer; |
| 25 | reader->view.size = 0; |
| 26 | reader->skip_mode = false; |
| 27 | reader->fd = fd; |
| 28 | } |
| 29 | |
| 30 | // Replaces the content of buffer with bytes from the file. |
| 31 | static int LoadFullBuffer(StackLineReader* reader) { |
Arvid Gerstmann | d968991 | 2018-05-04 09:32:17 +0200 | [diff] [blame] | 32 | const int read = CpuFeatures_ReadFile(reader->fd, reader->buffer, |
| 33 | STACK_LINE_READER_BUFFER_SIZE); |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 34 | assert(read >= 0); |
| 35 | reader->view.ptr = reader->buffer; |
| 36 | reader->view.size = read; |
| 37 | return read; |
| 38 | } |
| 39 | |
| 40 | // Appends with bytes from the file to buffer, filling the remaining space. |
| 41 | static int LoadMore(StackLineReader* reader) { |
| 42 | char* const ptr = reader->buffer + reader->view.size; |
| 43 | const size_t size_to_read = STACK_LINE_READER_BUFFER_SIZE - reader->view.size; |
Arvid Gerstmann | a1ffdcb | 2018-04-26 10:31:03 +0200 | [diff] [blame] | 44 | const int read = CpuFeatures_ReadFile(reader->fd, ptr, size_to_read); |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 45 | assert(read >= 0); |
| 46 | assert(read <= (int)size_to_read); |
| 47 | reader->view.size += read; |
| 48 | return read; |
| 49 | } |
| 50 | |
| 51 | static int IndexOfEol(StackLineReader* reader) { |
Arvid Gerstmann | a1ffdcb | 2018-04-26 10:31:03 +0200 | [diff] [blame] | 52 | return CpuFeatures_StringView_IndexOfChar(reader->view, '\n'); |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | // Relocate buffer's pending bytes at the beginning of the array and fills the |
| 56 | // remaining space with bytes from the file. |
| 57 | static int BringToFrontAndLoadMore(StackLineReader* reader) { |
| 58 | if (reader->view.size && reader->view.ptr != reader->buffer) { |
| 59 | memmove(reader->buffer, reader->view.ptr, reader->view.size); |
| 60 | } |
| 61 | reader->view.ptr = reader->buffer; |
| 62 | return LoadMore(reader); |
| 63 | } |
| 64 | |
| 65 | // Loads chunks of buffer size from disks until it contains a newline character |
| 66 | // or end of file. |
| 67 | static void SkipToNextLine(StackLineReader* reader) { |
| 68 | for (;;) { |
| 69 | const int read = LoadFullBuffer(reader); |
| 70 | if (read == 0) { |
| 71 | break; |
| 72 | } else { |
| 73 | const int eol_index = IndexOfEol(reader); |
| 74 | if (eol_index >= 0) { |
Arvid Gerstmann | d968991 | 2018-05-04 09:32:17 +0200 | [diff] [blame] | 75 | reader->view = |
| 76 | CpuFeatures_StringView_PopFront(reader->view, eol_index + 1); |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 77 | break; |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | static LineResult CreateLineResult(bool eof, bool full_line, StringView view) { |
| 84 | LineResult result; |
| 85 | result.eof = eof; |
| 86 | result.full_line = full_line; |
| 87 | result.line = view; |
| 88 | return result; |
| 89 | } |
| 90 | |
| 91 | // Helper methods to provide clearer semantic in StackLineReader_NextLine. |
| 92 | static LineResult CreateEOFLineResult(StringView view) { |
| 93 | return CreateLineResult(true, true, view); |
| 94 | } |
| 95 | |
| 96 | static LineResult CreateTruncatedLineResult(StringView view) { |
| 97 | return CreateLineResult(false, false, view); |
| 98 | } |
| 99 | |
| 100 | static LineResult CreateValidLineResult(StringView view) { |
| 101 | return CreateLineResult(false, true, view); |
| 102 | } |
| 103 | |
| 104 | LineResult StackLineReader_NextLine(StackLineReader* reader) { |
| 105 | if (reader->skip_mode) { |
| 106 | SkipToNextLine(reader); |
| 107 | reader->skip_mode = false; |
| 108 | } |
| 109 | { |
| 110 | const bool can_load_more = |
| 111 | reader->view.size < STACK_LINE_READER_BUFFER_SIZE; |
| 112 | int eol_index = IndexOfEol(reader); |
| 113 | if (eol_index < 0 && can_load_more) { |
| 114 | const int read = BringToFrontAndLoadMore(reader); |
| 115 | if (read == 0) { |
| 116 | return CreateEOFLineResult(reader->view); |
| 117 | } |
| 118 | eol_index = IndexOfEol(reader); |
| 119 | } |
| 120 | if (eol_index < 0) { |
| 121 | reader->skip_mode = true; |
| 122 | return CreateTruncatedLineResult(reader->view); |
| 123 | } |
| 124 | { |
Arvid Gerstmann | d968991 | 2018-05-04 09:32:17 +0200 | [diff] [blame] | 125 | StringView line = |
| 126 | CpuFeatures_StringView_KeepFront(reader->view, eol_index); |
| 127 | reader->view = |
| 128 | CpuFeatures_StringView_PopFront(reader->view, eol_index + 1); |
Guillaume Chatelet | 439d371 | 2018-02-01 10:03:09 +0100 | [diff] [blame] | 129 | return CreateValidLineResult(line); |
| 130 | } |
| 131 | } |
| 132 | } |