brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.8 KiB · 9de108e Raw
217 lines · c
1//===-- Writer definition for printf ----------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H10#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H11 12#include "src/__support/CPP/string_view.h"13#include "src/__support/macros/config.h"14#include "src/__support/macros/optimization.h"15#include "src/stdio/printf_core/core_structs.h"16#include "src/string/memory_utils/inline_memcpy.h"17#include "src/string/memory_utils/inline_memset.h"18 19#include <stddef.h>20 21namespace LIBC_NAMESPACE_DECL {22namespace printf_core {23 24enum class WriteMode {25  FILL_BUFF_AND_DROP_OVERFLOW,26  FLUSH_TO_STREAM,27  RESIZE_AND_FILL_BUFF,28  RUNTIME_DISPATCH,29};30 31// Helper to omit the template argument if we are using runtime dispatch and32// avoid multiple copies of the converter functions.33template <WriteMode write_mode> struct Mode {34#ifdef LIBC_COPT_PRINTF_RUNTIME_DISPATCH35  static constexpr WriteMode value = WriteMode::RUNTIME_DISPATCH;36#else37  static constexpr WriteMode value = write_mode;38#endif39};40 41template <WriteMode write_mode> struct WriteBuffer {42  using StreamWriter = int (*)(cpp::string_view, void *);43  char *buff;44  const char *init_buff; // for checking when resize.45  size_t buff_len;46  size_t buff_cur = 0;47 48  // The stream writer will be called when the buffer is full. It will be passed49  // string_views to write to the stream.50  const StreamWriter stream_writer;51  void *output_target;52 53  // The current writing mode in case the user wants runtime dispatch of the54  // stream writer with function pointers.55  [[maybe_unused]] WriteMode write_mode_;56 57  LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook,58                          void *target)59      : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),60        output_target(target), write_mode_(WriteMode::FLUSH_TO_STREAM) {}61 62  LIBC_INLINE WriteBuffer(char *buff, size_t buff_len)63      : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(nullptr),64        output_target(nullptr),65        write_mode_(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}66 67  LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook)68      : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),69        output_target(this), write_mode_(WriteMode::RESIZE_AND_FILL_BUFF) {}70 71  LIBC_INLINE int flush_to_stream(cpp::string_view new_str) {72    if (buff_cur > 0) {73      int retval = stream_writer({buff, buff_cur}, output_target);74      if (retval < 0)75        return retval;76    }77    if (new_str.size() > 0) {78      int retval = stream_writer(new_str, output_target);79      if (retval < 0)80        return retval;81    }82    buff_cur = 0;83    return WRITE_OK;84  }85 86  LIBC_INLINE int fill_remaining_to_buff(cpp::string_view new_str) {87    if (buff_cur < buff_len) {88      size_t bytes_to_write = buff_len - buff_cur;89      if (bytes_to_write > new_str.size()) {90        bytes_to_write = new_str.size();91      }92      inline_memcpy(buff + buff_cur, new_str.data(), bytes_to_write);93      buff_cur += bytes_to_write;94    }95    return WRITE_OK;96  }97 98  LIBC_INLINE int resize_and_write(cpp::string_view new_str) {99    return stream_writer(new_str, output_target);100  }101 102  // The overflow_write method is intended to be called to write the contents of103  // the buffer and new_str to the stream_writer if it exists. If a resizing104  // hook is provided, it will resize the buffer and write the contents. If105  // neither a stream_writer nor a resizing hook is provided, it will fill the106  // remaining space in the buffer with new_str and drop the overflow. Calling107  // this with an empty string will flush the buffer if relevant.108 109  LIBC_INLINE int overflow_write(cpp::string_view new_str) {110    if constexpr (write_mode == WriteMode::RUNTIME_DISPATCH) {111      if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)112        return fill_remaining_to_buff(new_str);113      else if (write_mode_ == WriteMode::FLUSH_TO_STREAM)114        return flush_to_stream(new_str);115      else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF)116        return resize_and_write(new_str);117    } else if constexpr (write_mode == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {118      return fill_remaining_to_buff(new_str);119    } else if constexpr (write_mode == WriteMode::FLUSH_TO_STREAM) {120      return flush_to_stream(new_str);121    } else if constexpr (write_mode == WriteMode::RESIZE_AND_FILL_BUFF) {122      return resize_and_write(new_str);123    }124    __builtin_unreachable();125  }126};127 128template <WriteMode write_mode> class Writer final {129  WriteBuffer<write_mode> &wb;130  size_t chars_written = 0;131 132  LIBC_INLINE int pad(char new_char, size_t length) {133    // First, fill as much of the buffer as possible with the padding char.134    size_t written = 0;135    const size_t buff_space = wb.buff_len - wb.buff_cur;136    // ASSERT: length > buff_space137    if (buff_space > 0) {138      inline_memset(wb.buff + wb.buff_cur, new_char, buff_space);139      wb.buff_cur += buff_space;140      written = buff_space;141    }142 143    // Next, overflow write the rest of length using the mini_buff.144    constexpr size_t MINI_BUFF_SIZE = 64;145    char mini_buff[MINI_BUFF_SIZE];146    inline_memset(mini_buff, new_char, MINI_BUFF_SIZE);147    cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE);148    while (written + MINI_BUFF_SIZE < length) {149      int result = wb.overflow_write(mb_string_view);150      if (result != WRITE_OK)151        return result;152      written += MINI_BUFF_SIZE;153    }154    cpp::string_view mb_substr = mb_string_view.substr(0, length - written);155    return wb.overflow_write(mb_substr);156  }157 158public:159  LIBC_INLINE Writer(WriteBuffer<write_mode> &wb) : wb(wb) {}160 161  // Takes a string, copies it into the buffer if there is space, else passes it162  // to the overflow mechanism to be handled separately.163  LIBC_INLINE int write(cpp::string_view new_string) {164    chars_written += new_string.size();165    if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) {166      inline_memcpy(wb.buff + wb.buff_cur, new_string.data(),167                    new_string.size());168      wb.buff_cur += new_string.size();169      return WRITE_OK;170    }171    return wb.overflow_write(new_string);172  }173 174  // Takes a char and a length, memsets the next length characters of the buffer175  // if there is space, else calls pad which will loop and call the overflow176  // mechanism on a secondary buffer.177  LIBC_INLINE int write(char new_char, size_t length) {178    chars_written += length;179 180    if (LIBC_LIKELY(wb.buff_cur + length <= wb.buff_len)) {181      inline_memset(wb.buff + wb.buff_cur, static_cast<unsigned char>(new_char),182                    length);183      wb.buff_cur += length;184      return WRITE_OK;185    }186    return pad(new_char, length);187  }188 189  // Takes a char, copies it into the buffer if there is space, else passes it190  // to the overflow mechanism to be handled separately.191  LIBC_INLINE int write(char new_char) {192    chars_written += 1;193    if (LIBC_LIKELY(wb.buff_cur + 1 <= wb.buff_len)) {194      wb.buff[wb.buff_cur] = new_char;195      wb.buff_cur += 1;196      return WRITE_OK;197    }198    cpp::string_view char_string_view(&new_char, 1);199    return wb.overflow_write(char_string_view);200  }201 202  LIBC_INLINE size_t get_chars_written() { return chars_written; }203};204 205// Class-template auto deduction helpers.206Writer(WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)207    -> Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;208Writer(WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>)209    -> Writer<WriteMode::RESIZE_AND_FILL_BUFF>;210Writer(WriteBuffer<WriteMode::FLUSH_TO_STREAM>)211    -> Writer<WriteMode::FLUSH_TO_STREAM>;212 213} // namespace printf_core214} // namespace LIBC_NAMESPACE_DECL215 216#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H217