Buffer handle – v1.0.1 Perform elementary actions on buffers with a single function code.malloc.fr

Reference

All the code is scoped in the buffer_handle namespace.


Basic types

//Declared in buffer_handle/action.hpp
    enum class action { size, prepare, write, reset };
    
    //Declared in buffer_handle/config.hpp
    enum class config { static_, dynamic };
Main Misc Helpers
Bitset Conditions Adapters
Boolean Format ~ itoa
Character Modifiers
Container Separators
Date Nothing ~ container
Number Padding
Time Resetting
Timezone
String

Bitset

Handle a set of values represented by a value in the power of 2. It requires a set and a separator contract object respectively to known the number of elements and their conversion to string, and to eventually separate them with a specific pattern.

//Defined in buffer_handle/bitset.hpp
    
    template<class Set, action Action, class Separator>
    char * bitset(char * buffer, typename Set::value_type value, Separator & separator);
    
    template<config Config, align Align, char Pad, class Set, action Action, class Separator>
    char * bitset(char * buffer, typename Set::value_type value, std::size_t & max_length,
                  Separator & separator);
    
    template<config Config, align Align, char Pad, class Set, action Action, class Separator>
    char * bitset(char * buffer, typename Set::value_type value, std::size_t & max_length,
                  Separator & separator, std::size_t & previous_length);
    
    template<config Config, align Align, char Pad, class Set, bool IsLong = false>
    struct bitset_t
    {
      template<action Action, class Separator>
      char * handle(char * buffer, typename Set::value_type value, Separator & separator);
    };

Boolean

Handle a boolean as a string or as a single character. The content is written whenever must_write is true.

//Defined in buffer_handle/boolean.hpp
    
    template<config Config, case_ Case, align Align, char Pad, action Action>
    char * boolean(char * buffer, bool value);
    
    template<config Config, action Action, char False = '0', char True = '1'>
    char * boolean(char * buffer, bool value);

Character

Handle a single character or a given token. The content is written whenever must_write is true.

//Defined in buffer_handle/character.hpp
    
    template<config Config, action Action>
    char * character(char * buffer, char c);

Token

//Defined in buffer_handle/token.hpp
    
    template<config Config, action Action>
    char * TOKEN(char * buffer);
    //TOKEN = new_line, carriage_return, space, exclamation_mark, double_quote, single_quote,
    //        opening_parenthesis, closing_parenthesis, plus, comma, hyphen = minus, dot, slash,
    //        colon, semicolon, less_than, equal, greater_than, question_mark, opening_bracket,
    //        backslash, closing_bracket, underscore, backquote, opening_brace, pipe, closing_brace

Container

Handle a container following the std::advance order. They require a handler and a separator contract object respectively responsible for writing elements of the container to the buffer and to eventually separate them with a specific pattern.

Containers can be handled in two ways. The first function writes every element contained between begin and end in a buffer of size max_length but does not check out of bound data. The size must be known to not exceed the buffer size prior to calling the function. The second one writes the most possible elements between current and end and updates current if it is not possible to write every element. It checks the size of every element before printing it to the buffer and is thus slower.

//Defined in buffer_handle/container.hpp
    
    template<config Config, align Align, char Pad, action Action,
           class Handler, class Separator, class Iterator>
    char * container(char * buffer, const Iterator & begin, const Iterator & end, std::size_t max_length,
             Handler & handler, Separator & separator /* , std::size_t & previous_length */);
    
    template<align Align, char Pad, action Action,
           class Handler, class Separator, typename Iterator>
    char * container(char * buffer, const Iterator & begin, Iterator & current, const Iterator & end,
             std::size_t max_length, Handler & handler, Separator & separator);
    
    template<config Config, align Align, char Pad, bool IsLong = false>
    struct container_t
    {
      void set_max_length(std::size_t length);
    
      template<action Action, class Iterator, class Handler, class Separator>
      char * handle(char * buffer, Iterator & begin, Iterator & end,
            Handler & handler, Separator & separator);
    };

The maximum length is determined by the function, based on the content of the container, when static.

Date

The following functions handle asc, rfc822, rfc850 (§2.1.4), rfc1123 (§5.2.14), rfc5322 (§3.3) and rfc7231 (§7.1.1.1) dates.

Note that for functions accepting directly a month or a year, 1 is for January and years start at 0 not 1900.

namespace asc
    {
      template<config Config, action Action,
           typename Weekday, typename Day, typename Month, typename Year,
           typename Hours, typename Minutes, typename Seconds>
      char * date(char * buffer,
              Weekday weekday, Day day, Month month, Year year,
              Hours hours, Minutes minutes, Seconds seconds);
    
      template<config Config, action Action>
      char * date(char * buffer, std::tm date_time);
    };
    
    namespace rfc822//§5
    {
      template<config Config, bool HandleWeekday, bool HandleSeconds, class Timezone, action Action,
           typename Weekday, typename Day, typename Month, typename Year,
           typename Hours, typename Minutes, typename Seconds>
      char * date(char * buffer,
              Weekday weekday, Day day, Month month, Year year,
              Hours hours, Minutes minutes, Seconds seconds,
              const Timezone & timezone);
    
      template<config Config, bool HandleWeekday, bool HandleSeconds, class Timezone, action Action>
      char * date(char * buffer, std::tm date_time, const Timezone & timezone);
    };
    
    namespace rfc850//§2.1.4
    {
      template<config Config, class Timezone, action Action,
           typename Weekday, typename Day, typename Month, typename Year,
           typename Hours, typename Minutes, typename Seconds>
      char * date(char * buffer,
              Weekday weekday, Day day, Month month, Year year,
              Hours hours, Minutes minutes, Seconds seconds,
              const Timezone & timezone);
    
      template<config Config, class Timezone, action Action>
      char * date(char * buffer, std::tm date_time, const Timezone & timezone);
    };
    
    namespace rfc1123//§5.2.14
    {
      template<config Config, bool HandleWeekday, bool HandleSeconds, class Timezone, action Action,
           typename Weekday, typename Day, typename Month, typename Year,
           typename Hours, typename Minutes, typename Seconds>
      char * date(char * buffer,
              Weekday weekday, Day day, Month month, Year year,
              Hours hours, Minutes minutes, Seconds seconds,
              const Timezone & timezone);
    
      template<config Config, bool HandleWeekday, bool HandleSeconds, class Timezone, action Action>
      char * date(char * buffer, std::tm date_time, const Timezone & timezone);
    };
    
    namespace rfc5322//§3.3
    {
      template<config Config, action Action,
           typename Weekday, typename Day, typename Month, typename Year,
           typename Hours, typename Minutes, typename Seconds,
           typename TimezoneHours, typename TimezoneMinutes>
      char * date(char * buffer,
          Weekday weekday, Day day, Month month, Year year,
          Hours hours, Minutes minutes, Seconds seconds,
          bool timezone_sign, TimezoneHours timezone_hours, TimezoneMinutes timezone_minutes);
    
      template<config Config, action Action, typename TimezoneHours, typename TimezoneMinutes>
      char * date(char * buffer, std::tm date_time,
          bool timezone_sign, TimezoneHours timezone_hours, TimezoneMinutes timezone_minutes);
    };
    
    namespace rfc7231//§7.1.1.1
    {
      template<config Config, action Action,
           typename Weekday, typename Day, typename Month, typename Year,
           typename Hours, typename Minutes, typename Seconds>
      char * date(char * buffer,
          Weekday weekday, Day day, Month month, Year year,
          Hours hours, Minutes minutes, Seconds seconds);
    
      template<config Config, action Action>
      char * date(char * buffer, std::tm date_time);
    };

The two functions below are mainly used by above functions but can also can be used independtly. They respectively write the date in format ‘dd Mon YY’ and ‘Mon dd’.

The template parameter InsteadOfALeadingZeroForDay can be '\0' in order to have a leading zero while YearOn4Digits toggles the year format to ‘YYYY’ instead of ‘YY’.

//Defined in buffer_handle/date.hpp
    
    template<config Config, char InsteadOfALeadingZeroForDay, char Separator, bool YearOn4Digits,
         action Action, typename Day, typename Month, typename Year>
    char * day_month_year(char * buffer, Day day, Month month, Year year);// dd Mon YY
    
    template<config Config, action Action, typename Month, typename Day>
    char * month_day(char * buffer, Month month, Day day);// Mon dd

The fonctions below handle date components.

template<config Config, action Action, typename Weekday>
    char * wkday(char * buffer, Weekday weekday);// Sun-Sat
    
    template<config Config, action Action, typename Weekday>
    char * weekday(char * buffer, Weekday weekday);// Sunday-Saturday
    
    template<config Config, action Action, typename Month>
    char * month(char * buffer, Month month);// Jan-Dec

Number

//Defined in buffer_handle/number.hpp
    
    template<config Config, char InsteadOfALeadingZero, action Action, typename I>
    char * two_digits_number(char * buffer, I i);
    
    template<config Config, action Action, typename I>
    char * four_digits_number(char * buffer, I i);

Those two functions handle a specific and fixed number of digits of a positive integral number i. There are no verifications that the decimal representation of i is bounded to those digits nor that it is positive (except for an assert). They are mainly used to handle times and dates. The template parameter InsteadOfALeadingZero can be used to set the first digit to a specific character in case of a single digit number. Use '\0' in order to have a leading zero.

template<config Action, class Itoa, typename I>
    char * integral_number(char * buffer, I i, const Itoa & itoa = Itoa());
    
    template<config Config, align Align, char Pad, action Action, class Itoa,
         typename I, typename Digits = uint8_t>
    char * integral_number(char * buffer, I i, Digits & max_digits, /* Digits & previous_digits, */
                   const Itoa & itoa = Itoa());
    
    template<config Config, align Align, char Pad, class Itoa, typename I, typename Digits,
         bool IsLong = false>
    struct integral_number_t
    {
      template<action Action>
      char * handle(char * buffer, I value, const Itoa & itoa = Itoa());
    };

The first function above writes a positive integral number to the buffer when prepared. The second one handles a positive integral number whose decimal representation is limited to max_digits. This argument would usually be assigned by the function when a buffer is prepared based on the maximum possible value supplied in i, in which case it must not be modified afterwards. It is also possible to provide a constant value throughout calls if this value is already known. A uint8_t should be enough to encode the number of digits for most applications but this type could modified for bigger values. The Itoa functor must conform to the adapter contract and the function without the max_digits argument is equivalent to calling its counterpart as static.

Time

These functions handle the three classical time formats: epoch, ‘HH:mm’ and ‘HH:mm:ss’.

//Defined in buffer_handle/time.hpp
    
    template<config Config, align Align, char Pad, action Action, class Itoa, typename Digits = uint8_t>
    char * time_(char * buffer, time_t time, Digits & max_digits, const Itoa & itoa = Itoa());
    
    template<config Config, action Action, typename Hours, typename Minutes>
    char * time_(char * buffer, Hours hours, Minutes minutes);
    
    template<config Config, action Action, typename Hours, typename Minutes, typename Seconds>
    char * time_(char * buffer, Hours hours, Minutes minutes, Seconds seconds);
    
    template<config Config, action Action>
    char * time_(char * buffer, std::tm time);

The Itoa functor must conform to the adapter contract.

Timezone

Those functions and functors handle universal, North American, military and differential timezones.

//Defined in buffer_handle/timezone.hpp
    
    enum class universal_timezone : uint8_t { UT, GMT };
    
    template<config Config, align Align, char Pad, action Action>
    char * universal_timezone(char * buffer, enum universal_timezone value);
    
    enum class north_american_timezone : uint8_t { EST, EDT, CST, CDT, MST, MDT, PST, PDT };
    
    template<config Config, action Action>
    char * north_american_timezone(char * buffer, enum north_american_timezone value);
    
    template<config Config, action Action>
    char * military_timezone(char * buffer, char letter);
    
    template<config Config, action Action, typename T>
    char * military_timezone(char * buffer, T offset);
    
    template<config Config, action Action, typename Hours, typename Minutes>
    char * differential_timezone(char * buffer, bool sign, Hours hours, Minutes minutes);
    
    template<config Config, align Align, char Pad>
    struct universal_timezone_t
    {
      universal_timezone_t(enum universal_timezone value = universal_timezone::GMT);
    
      enum universal_timezone value;
    
      template<action Action>
      char * handle(char * buffer) const;
    };
    
    template<config Config>
    struct north_american_timezone_t
    {
      north_american_timezone_t(enum north_american_timezone value = north_american_timezone::EST);
    
      enum north_american_timezone value;
    
      template<action Action>
      char * handle(char * buffer) const;
    };
    
    template<config Config>
    struct military_timezone_t
    {
      military_timezone_t(char letter = 'Z');
    
      template<typename T>
      military_timezone_t(T offset);
    
      template<typename T>
      void set(T offset);
    
      char letter;
    
      template<action Action>
      char * handle(char * buffer) const;
    };
    
    template<config Config>
    struct differential_timezone_t
    {
      differential_timezone_t(bool positive = true, uint8_t hours = 0, uint8_t minutes = 0);
    
      bool positive;
      uint8_t hours;
      uint8_t minutes;
    
      template<action Action>
      char * handle(char * buffer) const;
    };

String

//Defined in buffer_handle/string.hpp
    
    template<config Config, action Action>
    char * string(char * buffer, const char * value, std::size_t length = std::strlen(value));

Handles a string in the most basic way by copying it to the buffer when must_write is true. The length argument conveniently defaults to the length of the string pointed to by value. However, a verification against a nullptr is only performed, as an ASSERT, for a static configuration. In a dynamic configuration, nullptr can be passed during a size or prepare (for instance if the actual string is not known yet) as long as the same length is supplied for every action. Also note that the reset action uses the value passed at that time. To reset a buffer to a specific value use the function below.

template<config Config, align Align, char Pad, action Action>
    char * string(char * buffer, const char * value, std::size_t length, std::size_t max_length
              /* , std::size_t & previous_length */);

In this overload, the max_length argument sets the maximum size of the buffer. It must remain the same for every action. For simplification, passing a nullptr for a write is equivalent to a reset. When reseting, the string is filled with Pad characters. For an explanation on the optional previous_length argument, refer to the large buffer section.

Manual management

In order to avoid copying the value to the buffer everytime must_write is true, the overloads below can be used to assign value to the location in the buffer when prepared. The value is assigned either to the left or to the right of the underlying buffer depending on the case. If nullptr is passed then the call defaults to the previous functions with nullptr as the value.

template<config Config, action Action>
    char * string(char * buffer, char ** value, std::size_t length);
    
    template<config Config, align Align, char Pad, action Action>
    char * string(char * buffer, char ** value, std::size_t length, std::size_t max_length);
    
    template<config Config, align Align, char Pad, bool IsLong = false>
    struct string_t
    {
      template<action Action>
      char * handle(char * buffer, const char * value, std::size_t length) const;
    };

Conditions

Must write

The function must_write can be used to test whether the given configuration and action template parameters imply to write something to the buffer. It is true:

//Defined in buffer_handle/helper.hpp
    
    constexpr bool must_write(config Config, action Action);

Format

//Declared in buffer_handle/align.hpp
    enum class align { left, right };
    
    //Declared in buffer_handle/case.hpp
    enum class case_ { lower, first_upper, upper };

Modifiers

It is sometimes necessary to modify the behavior of a given action to adjust a function to a specific external behavior.

The function write_when_reset can be used to bypass a reset with a write (see date.hcp for an example).

The function always_write can be used in a dynamic context to force a write into a reset actually rewriting the content everytime as if it were static. It can be used when in a dynamic configuration to write a recurring value to a position that may change depending on the situation.

//Defined in buffer_handle/helper.hpp
    
    constexpr action write_when_reset(action value);
    constexpr action always_write(action value);

Nothing

//Defined in buffer_handle/nothing.hpp
    
    struct nothing_t
    {
      template<action Action>
      char * handle(char * buffer) const;
    };

Padding

The pad_left and pad_right functions could be used to fill the left, respectively right, side of a buffer. For pad_left, the content to pad is between begin and end while for pad_right it is between end and begin + max_length. If UsePreviousLength is false then the memset will happen on these regions. However, if it is true, the previous_length argument will be compared to the current length. Then, if the previous length is smaller than the current length, no memset is required since the new content entirely overwrites the previous content. On the other hand, if the previous length is strictly bigger than the current length, the differential region between the previous and the actual content is reset by memset.

//Defined in buffer_handle/helper.hpp
    
    template<bool UsePreviousLength, char Pad, typename Size>
    void pad_left(char * begin, char * end, Size max_length, Size & previous_length);
    
    template<bool UsePreviousLength, char Pad, typename Size>
    void pad_right(char * begin, char * end, Size max_length, Size & previous_length);

Resetting

The reset function is used to reset a buffer depending on alignment and usage of the previous length.

template<align Align, bool UsePreviousLength, char Pad, typename Size>
    void reset(char * buffer, Size max_length, Size & previous_length);

Adapters

Itoa

The Itoa adapters have to respect the following contract.

namespace adapter
    {
      namespace itoa
      {
        struct NAME
        {
          template<typename I>
          char * fwd(char * buffer, I i) const;
    
          template<typename I>
          char * bwd(char * buffer, I i) const;
        };
      };
    };

Available implementations are:

Separators

Container

Functors character_separator_t and character_and_space_separator_t can be used as the Separator template parameter for containers.

//Defined in buffer_handle/helper.hpp
    
    template<char Separator>
    struct character_separator_t
    {
      template<config Config, action Action>
      char * handle(char * buffer) const;
    };
    
    template<char Separator>
    struct character_and_space_separator_t
    {
      template<config Config, action Action>
      char * handle(char * buffer) const;
    };