getline_interposer.cpp (2615B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * Interposing getline because of 7 * https://bugzilla.mozilla.org/show_bug.cgi?id=914190 8 */ 9 10 #ifdef __ANDROID__ 11 12 # include <cstdlib> 13 # include <cstdio> 14 # include <cerrno> 15 16 # include <limits> 17 18 namespace { 19 20 // RAII on file locking. 21 class FileLocker { 22 FILE* stream; 23 24 public: 25 explicit FileLocker(FILE* stream) : stream(stream) { flockfile(stream); } 26 ~FileLocker() { funlockfile(stream); } 27 }; 28 29 ssize_t internal_getdelim(char** __restrict lineptr, size_t* __restrict n, 30 int delim, FILE* __restrict stream) { 31 constexpr size_t n_default = 64; 32 constexpr size_t n_max = 33 std::numeric_limits<ssize_t>::max() < std::numeric_limits<size_t>::max() 34 ? (size_t)std::numeric_limits<ssize_t>::max() + 1 35 : std::numeric_limits<size_t>::max(); 36 constexpr size_t n_limit = 2 * ((n_max - 1) / 3); 37 38 if (!lineptr || !n || !stream) { 39 errno = EINVAL; 40 return -1; 41 } 42 43 // Lock the file so that we can us unlocked getc in the inner loop. 44 FileLocker fl(stream); 45 46 if (!*lineptr || *n == 0) { 47 *n = n_default; 48 if (auto* new_lineptr = reinterpret_cast<char*>(realloc(*lineptr, *n))) { 49 *lineptr = new_lineptr; 50 } else { 51 errno = ENOMEM; 52 return -1; 53 } 54 } 55 56 ssize_t result; 57 size_t cur_len = 0; 58 59 while (true) { 60 // Retrieve an extra char. 61 int i = getc_unlocked(stream); 62 if (i == EOF) { 63 result = -1; 64 break; 65 } 66 67 // Eventually grow the buffer. 68 if (cur_len + 1 >= *n) { 69 size_t needed = *n >= n_limit ? n_max : 3 * *n / 2 + 1; 70 71 if (cur_len + 1 >= needed) { 72 errno = EOVERFLOW; 73 return -1; 74 } 75 76 if (auto* new_lineptr = (char*)realloc(*lineptr, needed)) { 77 *lineptr = new_lineptr; 78 } else { 79 errno = ENOMEM; 80 return -1; 81 } 82 *n = needed; 83 } 84 85 (*lineptr)[cur_len] = i; 86 cur_len++; 87 88 if (i == delim) break; 89 } 90 (*lineptr)[cur_len] = '\0'; 91 return cur_len ? cur_len : result; 92 } 93 94 } // namespace 95 96 extern "C" { 97 98 MFBT_API ssize_t getline(char** __restrict lineptr, size_t* __restrict n, 99 FILE* __restrict stream) { 100 return internal_getdelim(lineptr, n, '\n', stream); 101 } 102 103 MFBT_API ssize_t getdelim(char** __restrict lineptr, size_t* __restrict n, 104 int delim, FILE* __restrict stream) { 105 return internal_getdelim(lineptr, n, delim, stream); 106 } 107 108 } // extern "C" 109 110 #endif