| // Copyright 2017 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "internal/stack_line_reader.h" | |
| #include <assert.h> | |
| #include <errno.h> | |
| #include <stdio.h> | |
| #include "internal/filesystem.h" | |
| void StackLineReader_Initialize(StackLineReader* reader, int fd) { | |
| reader->view.ptr = reader->buffer; | |
| reader->view.size = 0; | |
| reader->skip_mode = false; | |
| reader->fd = fd; | |
| } | |
| // Replaces the content of buffer with bytes from the file. | |
| static int LoadFullBuffer(StackLineReader* reader) { | |
| const int read = CpuFeatures_ReadFile(reader->fd, reader->buffer, | |
| STACK_LINE_READER_BUFFER_SIZE); | |
| assert(read >= 0); | |
| reader->view.ptr = reader->buffer; | |
| reader->view.size = read; | |
| return read; | |
| } | |
| // Appends with bytes from the file to buffer, filling the remaining space. | |
| static int LoadMore(StackLineReader* reader) { | |
| char* const ptr = reader->buffer + reader->view.size; | |
| const size_t size_to_read = STACK_LINE_READER_BUFFER_SIZE - reader->view.size; | |
| const int read = CpuFeatures_ReadFile(reader->fd, ptr, size_to_read); | |
| assert(read >= 0); | |
| assert(read <= (int)size_to_read); | |
| reader->view.size += read; | |
| return read; | |
| } | |
| static int IndexOfEol(StackLineReader* reader) { | |
| return CpuFeatures_StringView_IndexOfChar(reader->view, '\n'); | |
| } | |
| // Relocate buffer's pending bytes at the beginning of the array and fills the | |
| // remaining space with bytes from the file. | |
| static int BringToFrontAndLoadMore(StackLineReader* reader) { | |
| if (reader->view.size && reader->view.ptr != reader->buffer) { | |
| memmove(reader->buffer, reader->view.ptr, reader->view.size); | |
| } | |
| reader->view.ptr = reader->buffer; | |
| return LoadMore(reader); | |
| } | |
| // Loads chunks of buffer size from disks until it contains a newline character | |
| // or end of file. | |
| static void SkipToNextLine(StackLineReader* reader) { | |
| for (;;) { | |
| const int read = LoadFullBuffer(reader); | |
| if (read == 0) { | |
| break; | |
| } else { | |
| const int eol_index = IndexOfEol(reader); | |
| if (eol_index >= 0) { | |
| reader->view = | |
| CpuFeatures_StringView_PopFront(reader->view, eol_index + 1); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| static LineResult CreateLineResult(bool eof, bool full_line, StringView view) { | |
| LineResult result; | |
| result.eof = eof; | |
| result.full_line = full_line; | |
| result.line = view; | |
| return result; | |
| } | |
| // Helper methods to provide clearer semantic in StackLineReader_NextLine. | |
| static LineResult CreateEOFLineResult(StringView view) { | |
| return CreateLineResult(true, true, view); | |
| } | |
| static LineResult CreateTruncatedLineResult(StringView view) { | |
| return CreateLineResult(false, false, view); | |
| } | |
| static LineResult CreateValidLineResult(StringView view) { | |
| return CreateLineResult(false, true, view); | |
| } | |
| LineResult StackLineReader_NextLine(StackLineReader* reader) { | |
| if (reader->skip_mode) { | |
| SkipToNextLine(reader); | |
| reader->skip_mode = false; | |
| } | |
| { | |
| const bool can_load_more = | |
| reader->view.size < STACK_LINE_READER_BUFFER_SIZE; | |
| int eol_index = IndexOfEol(reader); | |
| if (eol_index < 0 && can_load_more) { | |
| const int read = BringToFrontAndLoadMore(reader); | |
| if (read == 0) { | |
| return CreateEOFLineResult(reader->view); | |
| } | |
| eol_index = IndexOfEol(reader); | |
| } | |
| if (eol_index < 0) { | |
| reader->skip_mode = true; | |
| return CreateTruncatedLineResult(reader->view); | |
| } | |
| { | |
| StringView line = | |
| CpuFeatures_StringView_KeepFront(reader->view, eol_index); | |
| reader->view = | |
| CpuFeatures_StringView_PopFront(reader->view, eol_index + 1); | |
| return CreateValidLineResult(line); | |
| } | |
| } | |
| } |