Revision control

Copy as Markdown

Other Tools

/**↩
*↩
* Copyright 2021-2023 Ribose Inc. (https://www.ribose.com)↩
*↩
* Permission is hereby granted, free of charge, to any person obtaining a copy of↩
* this software and associated documentation files (the "Software"), to deal in↩
* the Software without restriction, including without limitation the rights to↩
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of↩
* the Software, and to permit persons to whom the Software is furnished to do so,↩
* subject to the following conditions:↩
*↩
* The above copyright notice and this permission notice shall be included in all↩
* copies or substantial portions of the Software.↩
*↩
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR↩
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS↩
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR↩
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER↩
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN↩
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.↩
*↩
* Original copyright↩
*↩
* SEXP standard header file: sexp.h↩
* Ronald L. Rivest↩
* 6/29/1997↩
*/
#pragma once↩
#include <inttypes.h>↩
#include <climits>↩
#include <limits>↩
#include <cctype>↩
#include <locale>↩
#include <cstring>↩
#include <memory>↩
#include <algorithm>↩
#include <iostream>↩
#include <string>↩
#include <vector>↩
#include <cassert>↩
#include "sexp-public.h"
#include "sexp-error.h"
namespace sexp {↩
/*↩
* SEXP octet_t definitions↩
* We maintain some presumable redundancy with ctype↩
* However, we do enforce 'C' locale this way↩
*/
class SEXP_PUBLIC_SYMBOL sexp_char_defs_t {↩
protected:↩
static const bool base64digit[256]; /* true if c is base64 digit */
static const bool tokenchar[256]; /* true if c can be in a token */
static const unsigned char values[256][3]; /* values of c as { dec. hex, base64 } digit */
static std::locale c_locale;↩
static bool is_white_space(int c)↩
{↩
return c >= 0 && c <= 255 && std::isspace((char) c, c_locale);↩
};↩
static bool is_dec_digit(int c)↩
{↩
return c >= 0 && c <= 255 && std::isdigit((char) c, c_locale);↩
};↩
static bool is_hex_digit(int c)↩
{↩
return c >= 0 && c <= 255 && std::isxdigit((char) c, c_locale);↩
};↩
static bool is_base64_digit(int c) { return c >= 0 && c <= 255 && base64digit[c]; };↩
static bool is_token_char(int c) { return c >= 0 && c <= 255 && tokenchar[c]; };↩
static bool is_alpha(int c)↩
{↩
return c >= 0 && c <= 255 && std::isalpha((char) c, c_locale);↩
};↩
/* decvalue(c) is value of c as dec digit */
static unsigned char decvalue(int c) { return (c >= 0 && c <= 255) ? values[c][0] : 0; };↩
/* hexvalue(c) is value of c as a hex digit */
static unsigned char hexvalue(int c) { return (c >= 0 && c <= 255) ? values[c][1] : 0; };↩
/* base64value(c) is value of c as base64 digit */
static unsigned char base64value(int c)↩
{↩
return (c >= 0 && c <= 255) ? values[c][2] : 0;↩
};↩
};↩
class sexp_string_t;↩
class sexp_list_t;↩
class sexp_output_stream_t;↩
class sexp_input_stream_t;↩
/*↩
* SEXP simple string↩
*/
typedef uint8_t octet_t;↩
class SEXP_PUBLIC_SYMBOL sexp_simple_string_t : public std::basic_string<octet_t>,↩
private sexp_char_defs_t {↩
public:↩
sexp_simple_string_t(void) = default;↩
sexp_simple_string_t(const octet_t *dt) : std::basic_string<octet_t>{dt} {}↩
sexp_simple_string_t(const octet_t *bt, size_t ln) : std::basic_string<octet_t>{bt, ln} {}↩
sexp_simple_string_t &append(int c)↩
{↩
(*this) += (octet_t)(c & 0xFF);↩
return *this;↩
}↩
// Returns length for printing simple string as a token↩
size_t advanced_length_token(void) const { return length(); }↩
// Returns length for printing simple string as a base64 string↩
size_t advanced_length_base64(void) const { return (2 + 4 * ((length() + 2) / 3)); }↩
// Returns length for printing simple string ss in quoted-string mode↩
size_t advanced_length_quoted(void) const { return (1 + length() + 1); }↩
// Returns length for printing simple string ss in hexadecimal mode↩
size_t advanced_length_hexadecimal(void) const { return (1 + 2 * length() + 1); }↩
size_t advanced_length(sexp_output_stream_t *os) const;↩
sexp_output_stream_t *print_canonical_verbatim(sexp_output_stream_t *os) const;↩
sexp_output_stream_t *print_advanced(sexp_output_stream_t *os) const;↩
sexp_output_stream_t *print_token(sexp_output_stream_t *os) const;↩
sexp_output_stream_t *print_quoted(sexp_output_stream_t *os) const;↩
sexp_output_stream_t *print_hexadecimal(sexp_output_stream_t *os) const;↩
sexp_output_stream_t *print_base64(sexp_output_stream_t *os) const;↩
bool can_print_as_quoted_string(void) const;↩
bool can_print_as_token(const sexp_output_stream_t *os) const;↩
bool operator==(const char *right) const noexcept
{↩
return length() == std::strlen(right) && std::memcmp(data(), right, length()) == 0;↩
}↩
bool operator!=(const char *right) const noexcept
{↩
return length() != std::strlen(right) || std::memcmp(data(), right, length()) != 0;↩
}↩
unsigned as_unsigned() const noexcept
{↩
return empty() ? std::numeric_limits<uint32_t>::max() :↩
(unsigned) atoi(reinterpret_cast<const char *>(c_str()));↩
}↩
};↩
inline bool operator==(const sexp_simple_string_t *left, const std::string &right) noexcept
{↩
return *left == right.c_str();↩
}↩
inline bool operator!=(const sexp_simple_string_t *left, const std::string &right) noexcept
{↩
return *left != right.c_str();↩
}↩
/*↩
* SEXP object↩
*/
class SEXP_PUBLIC_SYMBOL sexp_object_t {↩
public:↩
virtual ~sexp_object_t(){};↩
virtual sexp_output_stream_t *print_canonical(sexp_output_stream_t *os) const = 0;↩
virtual sexp_output_stream_t *print_advanced(sexp_output_stream_t *os) const;↩
virtual size_t advanced_length(sexp_output_stream_t *os) const = 0;↩
virtual sexp_list_t * sexp_list_view(void) noexcept { return nullptr; }↩
virtual sexp_string_t *sexp_string_view(void) noexcept { return nullptr; }↩
virtual bool is_sexp_list(void) const noexcept { return false; }↩
virtual bool is_sexp_string(void) const noexcept { return false; }↩
virtual const sexp_list_t *sexp_list_at(↩
std::vector<std::shared_ptr<sexp_object_t>>::size_type pos) const noexcept
{↩
return nullptr;↩
}↩
virtual const sexp_string_t *sexp_string_at(↩
std::vector<std::shared_ptr<sexp_object_t>>::size_type pos) const noexcept
{↩
return nullptr;↩
}↩
virtual const sexp_simple_string_t *sexp_simple_string_at(↩
std::vector<std::shared_ptr<sexp_object_t>>::size_type pos) const noexcept
{↩
return nullptr;↩
}↩
virtual bool operator==(const char *right) const noexcept { return false; }↩
virtual bool operator!=(const char *right) const noexcept { return true; }↩
virtual unsigned as_unsigned() const noexcept
{↩
return std::numeric_limits<uint32_t>::max();↩
}↩
};↩
/*↩
* SEXP string↩
*/
class SEXP_PUBLIC_SYMBOL sexp_string_t : public sexp_object_t {↩
protected:↩
bool with_presentation_hint;↩
sexp_simple_string_t presentation_hint;↩
sexp_simple_string_t data_string;↩
public:↩
sexp_string_t(const octet_t *dt) : with_presentation_hint(false), data_string(dt) {}↩
sexp_string_t(const octet_t *bt, size_t ln)↩
: with_presentation_hint(false), data_string(bt, ln)↩
{↩
}↩
sexp_string_t(const std::string &str)↩
: with_presentation_hint(false),↩
data_string(reinterpret_cast<const octet_t *>(str.data()))↩
{↩
}↩
sexp_string_t(void) : with_presentation_hint(false) {}↩
sexp_string_t(sexp_input_stream_t *sis) { parse(sis); };↩
const bool has_presentation_hint(void) const noexcept { return with_presentation_hint; }↩
const sexp_simple_string_t &get_string(void) const noexcept { return data_string; }↩
const sexp_simple_string_t &set_string(const sexp_simple_string_t &ss)↩
{↩
return data_string = ss;↩
}↩
const sexp_simple_string_t &get_presentation_hint(void) const noexcept
{↩
return presentation_hint;↩
}↩
const sexp_simple_string_t &set_presentation_hint(const sexp_simple_string_t &ph)↩
{↩
with_presentation_hint = true;↩
return presentation_hint = ph;↩
}↩
virtual sexp_output_stream_t *print_canonical(sexp_output_stream_t *os) const;↩
virtual sexp_output_stream_t *print_advanced(sexp_output_stream_t *os) const;↩
virtual size_t advanced_length(sexp_output_stream_t *os) const;↩
virtual sexp_string_t *sexp_string_view(void) noexcept { return this; }↩
virtual bool is_sexp_string(void) const noexcept { return true; }↩
virtual bool operator==(const char *right) const noexcept { return data_string == right; }↩
virtual bool operator!=(const char *right) const noexcept { return data_string != right; }↩
void parse(sexp_input_stream_t *sis);↩
virtual unsigned as_unsigned() const noexcept { return data_string.as_unsigned(); }↩
};↩
inline bool operator==(const sexp_string_t *left, const std::string &right) noexcept
{↩
return *left == right.c_str();↩
}↩
inline bool operator!=(const sexp_string_t *left, const std::string &right) noexcept
{↩
return *left != right.c_str();↩
}↩
/*↩
* SEXP list↩
*/
class SEXP_PUBLIC_SYMBOL sexp_list_t : public sexp_object_t,↩
public std::vector<std::shared_ptr<sexp_object_t>> {↩
public:↩
virtual ~sexp_list_t() {}↩
virtual sexp_output_stream_t *print_canonical(sexp_output_stream_t *os) const;↩
virtual sexp_output_stream_t *print_advanced(sexp_output_stream_t *os) const;↩
virtual size_t advanced_length(sexp_output_stream_t *os) const;↩
virtual sexp_list_t *sexp_list_view(void) noexcept { return this; }↩
virtual bool is_sexp_list(void) const noexcept { return true; }↩
virtual const sexp_list_t *sexp_list_at(size_type pos) const noexcept
{↩
return pos < size() ? (*at(pos)).sexp_list_view() : nullptr;↩
}↩
virtual const sexp_string_t *sexp_string_at(size_type pos) const noexcept
{↩
return pos < size() ? (*at(pos)).sexp_string_view() : nullptr;↩
}↩
const sexp_simple_string_t *sexp_simple_string_at(size_type pos) const noexcept
{↩
auto s = sexp_string_at(pos);↩
return s != nullptr ? &s->get_string() : nullptr;↩
}↩
void parse(sexp_input_stream_t *sis);↩
};↩
/*↩
sexp_depth_manager controls maximum allowed nesting of sexp lists↩
for sexp_input_stream, sexp_output_stream processing↩
One still can create an object with deeper nesting manually↩
*/
class SEXP_PUBLIC_SYMBOL sexp_depth_manager {↩
public:↩
static const size_t DEFAULT_MAX_DEPTH = 1024;↩
private:↩
size_t depth; /* current depth of nested SEXP lists */
size_t max_depth; /* maximum allowed depth of nested SEXP lists, 0 if no limit */
protected:↩
sexp_depth_manager(size_t m_depth = DEFAULT_MAX_DEPTH);↩
void reset_depth(size_t m_depth);↩
void increase_depth(int count = -1);↩
void decrease_depth(void);↩
};↩
/*↩
* SEXP input stream↩
*/
class SEXP_PUBLIC_SYMBOL sexp_input_stream_t : public sexp_char_defs_t, sexp_depth_manager {↩
protected:↩
std::istream *input_file;↩
uint32_t byte_size; /* 4 or 6 or 8 == currently scanning mode */
int next_char; /* character currently being scanned */
uint32_t bits; /* Bits waiting to be used */
uint32_t n_bits; /* number of such bits waiting to be used */
int count; /* number of 8-bit characters output by get_char */
virtual int read_char(void);↩
public:↩
sexp_input_stream_t(std::istream *i,↩
size_t max_depth = sexp_depth_manager::DEFAULT_MAX_DEPTH);↩
virtual ~sexp_input_stream_t() = default;↩
sexp_input_stream_t * set_input(std::istream *i,↩
size_t max_depth = sexp_depth_manager::DEFAULT_MAX_DEPTH);↩
sexp_input_stream_t * set_byte_size(uint32_t new_byte_size);↩
uint32_t get_byte_size(void) { return byte_size; }↩
sexp_input_stream_t * get_char(void);↩
sexp_input_stream_t * skip_white_space(void);↩
sexp_input_stream_t * skip_char(int c);↩
std::shared_ptr<sexp_object_t> scan_to_eof();↩
std::shared_ptr<sexp_object_t> scan_object(void);↩
std::shared_ptr<sexp_string_t> scan_string(void);↩
std::shared_ptr<sexp_list_t> scan_list(void);↩
sexp_simple_string_t scan_simple_string(void);↩
void scan_token(sexp_simple_string_t &ss);↩
void scan_verbatim_string(sexp_simple_string_t &ss, uint32_t length);↩
void scan_quoted_string(sexp_simple_string_t &ss, uint32_t length);↩
void scan_hexadecimal_string(sexp_simple_string_t &ss, uint32_t length);↩
void scan_base64_string(sexp_simple_string_t &ss, uint32_t length);↩
uint32_t scan_decimal_string(void);↩
int get_next_char(void) const { return next_char; }↩
int set_next_char(int c) { return next_char = c; }↩
sexp_input_stream_t *open_list(void);↩
sexp_input_stream_t *close_list(void);↩
};↩
/*↩
* SEXP output stream↩
*/
class SEXP_PUBLIC_SYMBOL sexp_output_stream_t : sexp_depth_manager {↩
public:↩
const uint32_t default_line_length = 75;↩
enum sexp_print_mode { /* PRINTING MODES */
canonical = 1, /* standard for hashing and tranmission */
base64 = 2, /* base64 version of canonical */
advanced = 3 /* pretty-printed */
};↩
protected:↩
std::ostream * output_file;↩
uint32_t base64_count; /* number of hex or base64 chars printed this region */
uint32_t byte_size; /* 4 or 6 or 8 depending on output mode */
uint32_t bits; /* bits waiting to go out */
uint32_t n_bits; /* number of bits waiting to go out */
sexp_print_mode mode; /* base64, advanced, or canonical */
uint32_t column; /* column where next character will go */
uint32_t max_column; /* max usable column, or 0 if no maximum */
uint32_t indent; /* current indentation level (starts at 0) */
public:↩
sexp_output_stream_t(std::ostream *o,↩
size_t max_depth = sexp_depth_manager::DEFAULT_MAX_DEPTH);↩
sexp_output_stream_t *set_output(std::ostream *o,↩
size_t max_depth = sexp_depth_manager::DEFAULT_MAX_DEPTH);↩
sexp_output_stream_t *put_char(int c); /* output a character */
sexp_output_stream_t *new_line(sexp_print_mode mode); /* go to next line (and indent) */
sexp_output_stream_t *var_put_char(int c);↩
sexp_output_stream_t *flush(void);↩
sexp_output_stream_t *print_decimal(uint64_t n);↩
sexp_output_stream_t *change_output_byte_size(int newByteSize, sexp_print_mode mode);↩
sexp_output_stream_t *print_canonical(const std::shared_ptr<sexp_object_t> &obj)↩
{↩
return obj->print_canonical(this);↩
}↩
sexp_output_stream_t *print_advanced(const std::shared_ptr<sexp_object_t> &obj)↩
{↩
return obj->print_advanced(this);↩
};↩
sexp_output_stream_t *print_base64(const std::shared_ptr<sexp_object_t> &obj);↩
sexp_output_stream_t *print_canonical(const sexp_simple_string_t *ss)↩
{↩
return ss->print_canonical_verbatim(this);↩
}↩
sexp_output_stream_t *print_advanced(const sexp_simple_string_t *ss)↩
{↩
return ss->print_advanced(this);↩
};↩
uint32_t get_byte_size(void) const { return byte_size; }↩
uint32_t get_column(void) const { return column; }↩
sexp_output_stream_t *reset_column(void)↩
{↩
column = 0;↩
return this;↩
}↩
uint32_t get_max_column(void) const { return max_column; }↩
sexp_output_stream_t *set_max_column(uint32_t mc)↩
{↩
max_column = mc;↩
return this;↩
}↩
sexp_output_stream_t *inc_indent(void)↩
{↩
++indent;↩
return this;↩
}↩
sexp_output_stream_t *dec_indent(void)↩
{↩
--indent;↩
return this;↩
}↩
sexp_output_stream_t *open_list(void)↩
{↩
put_char('(');↩
increase_depth();↩
return this;↩
}↩
sexp_output_stream_t *close_list(void)↩
{↩
put_char(')')->decrease_depth();↩
return this;↩
}↩
sexp_output_stream_t *var_open_list(void)↩
{↩
var_put_char('(')->increase_depth();↩
return this;↩
}↩
sexp_output_stream_t *var_close_list(void)↩
{↩
var_put_char(')')->decrease_depth();↩
return this;↩
}↩
};↩
} // namespace sexp↩