dodo  0.0.1
A C++ library to create containerized Linux services
bytes.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the dodo library (https://github.com/jmspit/dodo).
3  * Copyright (c) 2019 Jan-Marten Spit.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19  * @file bytes.cpp
20  * Implements the dodo::common::Bytes class.
21  */
22 
23 #include <cstring>
24 #include <iostream>
25 #include <openssl/evp.h>
26 #include <openssl/rand.h>
27 
28 #include "common/exception.hpp"
29 #include "common/bytes.hpp"
30 
31 namespace dodo::common {
32 
33  void Bytes::reserve( size_t sz ) {
34  size_t chunkedsz = ( sz / alloc_block + 1 ) * alloc_block;
35  if ( sz == size_ ) return;
36  if ( array_ ) {
37  if ( sz > allocated_size ) {
38  array_ = static_cast< Octet*>( std::realloc( array_, chunkedsz ) );
39  if ( array_ || sz == 0 ) {
40  size_ = sz;
41  allocated_size = chunkedsz;
42  if ( !sz ) array_ = nullptr;
43  } else throw_SystemException( "realloc of " << sz << " bytes failed", errno );
44  } else {
45  size_ = sz;
46  }
47  } else {
48  array_ = static_cast< Octet*>( std::malloc( chunkedsz ) );
49  if ( array_ ) {
50  size_ = sz;
51  allocated_size = chunkedsz;
52  } else throw_SystemException( "malloc of " << sz << " bytes failed", errno );
53  }
54  }
55 
56  void Bytes::free() {
57  if ( array_ != nullptr ) {
58  std::free( array_ );
59  array_ = nullptr;
60  }
61  size_ = 0;
62  allocated_size = 0;
63  }
64 
65  void Bytes::append( const Bytes& src ) {
66  size_t osize = size_;
67  this->reserve( size_ + src.size_ );
68  memcpy( array_ + osize, src.array_, src.size_ );
69  }
70 
71  void Bytes::append( const Bytes& src, size_t n ) {
72  if ( n > src.size_ ) throw_Exception( "cannot append more than available in src" );
73  size_t osize = size_;
74  this->reserve( size_ + n );
75  memcpy( array_ + osize, src.array_, n );
76  }
77 
78  void Bytes::append( const Octet* src, size_t n ) {
79  size_t osize = size_;
80  this->reserve( size_ + n );
81  memcpy( array_ + osize, src, n );
82  }
83 
84  void Bytes::append( Octet src ) {
85  this->reserve( size_ + 1 );
86  array_[size_ - 1 ] = src;
87  }
88 
89  Bytes& Bytes::operator=( const std::string &s ) {
90  this->reserve( s.length() );
91  for ( size_t i = 0; i < s.length(); i++ ) {
92  array_[i] = static_cast<Octet>( s[i] );
93  }
94  return *this;
95  }
96 
97  //Bytes::operator std::string() const {
98  std::string Bytes::asString() const {
99  std::stringstream ss;
100  for ( size_t i = 0; i < size_; i++ ) {
101  if ( array_[i] != 0 ) ss << array_[i];
102  else if ( i != size_ -1 ) throw_Exception( "Bytes contain an intermediate zero - string convesrion failed" );
103  }
104  return ss.str();
105  }
106 
107  void Bytes::random( size_t octets ) {
108  if ( octets != size_ ) {
109  this->reserve( octets );
110  }
111  RAND_bytes( array_, (int)size_ );
112  }
113 
114 
115  Bytes& Bytes::decodeBase64( const std::string& src ) {
116  Octet* temp = (Octet*)std::malloc( src.length() ); // too much, but safe, and temp anyway
117 
118 
119  EVP_ENCODE_CTX* ctx = EVP_ENCODE_CTX_new();
120  if ( !ctx ) throw_Exception( "EVP_ENCODE_CTX_new failed" );
121  EVP_DecodeInit( ctx );
122  int len = 0;
123  int rc = EVP_DecodeUpdate( ctx, temp, &len, (const unsigned char*)src.c_str(), (int)src.length() );
124  if ( rc == -1 ) throw_Exception( "EVP_DecodeUpdate failed" );
125  int actsize = len;
126  rc = EVP_DecodeFinal( ctx, temp + len, &len );
127  if ( rc != 1 ) throw_Exception( "EVP_DecodeFinal failed" );
128  actsize += len;
129  EVP_ENCODE_CTX_free( ctx );
130 
131  this->reserve( actsize );
132  memcpy( array_, temp, actsize );
133  std::free( temp );
134  return *this;
135  }
136 
137  std::string Bytes::encodeBase64() const {
138  size_t base64sz = size_;
139  base64sz = (base64sz / 48 + 1)*65+1;
140  unsigned char* target = (unsigned char*)std::malloc( base64sz );
141 
142  EVP_ENCODE_CTX* ctx = EVP_ENCODE_CTX_new();
143  if ( !ctx ) throw_Exception( "EVP_ENCODE_CTX_new failed" );
144  EVP_EncodeInit( ctx );
145  int len = 0;
146  int rc = EVP_EncodeUpdate( ctx, target, &len, array_, (int)size_ );
147  if ( rc != 1 ) throw_Exception( "EVP_EncodeUpdate failed" );
148  int actsize = len;
149  EVP_EncodeFinal( ctx, target + actsize, &len );
150  actsize += len;
151 
152  EVP_ENCODE_CTX_free( ctx );
153  std::stringstream ss;
154  for ( int i = 0; i < actsize; i++ ) {
155  char c = static_cast<unsigned char>( target[i] );
156  if ( c && c != '\n' ) ss << c;
157  }
158 
159  std::free( target );
160  return ss.str();
161  }
162 
163  Bytes::MatchType Bytes::match( const Bytes& other, size_t index, size_t &octets ) {
164  size_t local_size = size_ - index;
165  size_t match_size = std::min( local_size, other.size_ );
166  int cmp = memcmp( array_, other.array_, match_size );
167  if ( cmp == 0 ) {
168  if ( local_size < other.size_ ) {
169  octets = local_size;
170  return MatchType::Contained;
171  } else if ( local_size > other.size_ ) {
172  octets = other.size_;
173  return MatchType::Contains;
174  } else {
175  octets = local_size;
176  return MatchType::Full;
177  }
178  } else {
179  octets = 0;
180  return MatchType::Mismatch;
181  }
182  }
183 
184  std::string Bytes::hexDump( size_t n ) const {
185  std::stringstream ss;
186  for ( size_t i = 0; i < size_ && i < n; i++ ) {
187  if ( i != 0 ) ss << ":";
188  ss << std::setw(2) << std::hex << std::setfill('0') << (unsigned int)array_[i];
189  }
190  return ss.str();
191  }
192 
193 }
dodo::common::Bytes::size_
size_t size_
The array size in Octets (using an int as that is what OpenSSL uses)
Definition: bytes.hpp:210
dodo::common::Bytes::encodeBase64
std::string encodeBase64() const
Encodes the this Bytes into a base64-encoded string.
Definition: bytes.cpp:137
dodo::common::Bytes::alloc_block
const size_t alloc_block
Always allocate chunks of this size.
Definition: bytes.hpp:178
dodo::common::Bytes::decodeBase64
Bytes & decodeBase64(const std::string &src)
Decodes the base64 string into this Bytes.
Definition: bytes.cpp:115
dodo::common::Bytes::MatchType
MatchType
The way in which two Bytes instances match.
Definition: bytes.hpp:118
dodo::common::Bytes::free
void free()
Free and clear data.
Definition: bytes.cpp:56
dodo::common::Bytes::reserve
void reserve(size_t size)
Reserve memory in the Bytes.
Definition: bytes.cpp:33
dodo::common::Bytes
An array of Octets with size elements.
Definition: bytes.hpp:44
dodo::common::Bytes::append
void append(const Bytes &src)
Append another Bytes.
Definition: bytes.cpp:65
dodo::common::Bytes::random
void random(size_t octets)
Generate a random set of Octets.
Definition: bytes.cpp:107
dodo::common::Bytes::MatchType::Mismatch
@ Mismatch
The local array does not match the other array.
throw_SystemException
#define throw_SystemException(what, errno)
Throws an Exception with errno, passes FILE and LINE to constructor.
Definition: exception.hpp:188
throw_Exception
#define throw_Exception(what)
Throws an Exception, passes FILE and LINE to constructor.
Definition: exception.hpp:174
dodo::common::Bytes::array_
Octet * array_
The Octet array.
Definition: bytes.hpp:205
dodo::common::Bytes::MatchType::Contains
@ Contains
The local array contains the other array, but the local array has more data.
dodo::common
Common and utility interfaces.
Definition: application.hpp:29
dodo::common::Bytes::asString
std::string asString() const
Convert to a std::string.
Definition: bytes.cpp:98
dodo::common::Octet
uint8_t Octet
Octet aka 'byte'.
Definition: bytes.hpp:34
dodo::common::Bytes::match
MatchType match(const Bytes &other, size_t index, size_t &octets)
Match this Bytes from index to the other array (entirely).
Definition: bytes.cpp:163
dodo::common::Bytes::MatchType::Full
@ Full
The local and other array are equal in content and size.
dodo::common::Bytes::hexDump
std::string hexDump(size_t n) const
hex dump the first n octets of the data.
Definition: bytes.cpp:184
exception.hpp
dodo::common::Bytes::MatchType::Contained
@ Contained
The local array is contained in the other array, but the other array has more data.
dodo::common::Bytes::operator=
Bytes & operator=(const std::string &s)
Assign from std::string, not including a NULL terminator (so size will be string.length() ).
Definition: bytes.cpp:89
dodo::common::Bytes::allocated_size
size_t allocated_size
The allocated size, always >= size.
Definition: bytes.hpp:215
bytes.hpp