anthay / bytefluo

bytefluo is a C++ class for reading scalar values with specific byte order from byte streams

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

                              bytefluo


0  PURPOSE

bytefluo is a C++ class for reading simple integer scalar values with
specified byte order from a buffer, regardless of the native byte
order of the computer on which the code is running. It will throw an
exception if any attempt is made to access data beyond the specified
bounds of the buffer.

The class was created to simplify the low-level parsing of binary
data structures and to avoid having to write code such as

 uint16_t foo = static_cast<uint16_t>(*bar++) << 8;
 foo |= static_cast<uint16_t>(*bar++);

or

 uint16_t foo = ntohs(*(uint16_t *)bar);
 bar += 2;

and instead just write

 uint16_t foo;
 bar >> foo;

(where this bar is a bytefluo object rather than the raw pointer of
the previous examples). So, given some raw data, and a bytefluo
object through which we will access the data, such as

 unsigned char bytes[7] = { 0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF };
 bytefluo buf(bytes, bytes + sizeof(bytes), bytefluo::big);

we can then do things like this

 uint16_t a;
 uint8_t b;
 uint32_t c;

 buf >> a >> b >> c;

 assert(a == 0x99AA);
 assert(b == 0xBB);
 assert(c == 0xCCDDEEFF);


1  BUILDING

bytefluo is a C++ header-only library file; there are no other source
files to compile and no libs to link with. Just include bytefluo.h in
your code and start using it.


2  TESTING

The bytefluo library is distributed with a self-contained unit test in
the file bytefluo_test.cpp. Compile this file and run it.

OS X
 $ clang++ -std=c++11 -stdlib=libc++ -O3 -Wall -I. bytefluo_test.cpp
 $ ./a.out
 tests executed N, tests failed 0

Alternatively, use the makefile in build/clang

 $ cd build/clang/
 $ make test
 clang++ -std=c++11 -stdlib=libc++ -O3 -Wall -I../.. ../../bytefluo_test.cpp
  -o bytefluo_test
 ./bytefluo_test
 tests executed N, tests failed 0

Windows
 C:\test> cl /EHsc /W4 /O2 /nologo bytefluo_test.cpp
 C:\test> bytefluo_test
 tests executed N, tests failed 0

Alternatively, double-click the project file in build/msvc2013.

(Where N is the number of tests in the current version of test suite.)

The bytefluo library has been tested by the author under the following
compilers

 - clang++ 3.9.0 on OS X
 - Microsoft Visual C++ 2013 community edition on Windows


3  USING

The unit test source, bytefluo_test.cpp, contains many examples of use.
Here is another:

 #include "bytefluo.h"

 int main()
 {
     const unsigned char bytes[8] = {
         0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
     };
     bytefluo buf(bytes, bytes + sizeof(bytes), bytefluo::big);
     uint32_t x;
     buf >> x;  // x = 0x00112233
     buf.set_byte_order(bytefluo::little);
     buf >> x;  // x = 0x77665544
 }


3.1  OVERVIEW

The important things to know about bytefluo are

 - When you create a bytefluo object you tell it what data it will
   provide access to, and the initial byte order for any scalar
   reads that will be performed.
 - The bytefluo object does not hold a copy of the given data; it
   merely manages access to that data.
 - The bytefluo object maintains a cursor, which is set initially
   at the start of the managed data. All reads begin at the current
   cursor location and advance the cursor by the size of the data
   read.
 - If a requested read or seek would take the cursor outside the
   bounds of the managed data the read or seek does not take place,
   the state of the bytefluo object remains unchanged and the
   bytefluo object throws a bytefluo_exception object, which is
   derived from std::runtime_error.
 - Assuming that the bytefluo object is managing access to valid
   memory, we provide either the strong guarantee or the nothrow
   guarantee for all operations.
 - The bytefluo implementation is entriely within the header file,
   bytefluo.h. To use the class just #include "bytefluo.h".


3.2  DETAIL

3.2.1  CONSTRUCTION

 bytefluo(const void * begin, const void * end, byte_order bo)

The bytefluo object will manage access to the bytes in the half open
range [begin, end). Multi-byte scalars will be read assuming the given
byte order 'bo'. 'bo' must be one of

 bytefluo::big     most-significant byte has lowest address
 bytefluo::little  least-significant byte has lowest address

Throws bytefluo_exception if begin > end or if 'bo' is neither big
nor little.

Example:
 uint8_t foo[99];
 bytefluo buf(foo, foo + sizeof(foo), bytefluo::big);


3.2.2  DEFAULT CONSTRUCTION

 bytefluo()

The bytefluo object will manage access to the empty half open range
[0, 0), with scalar reads defaulting to big-endian. Throws nothing.

Example:
 bytefluo buf;
 size_t siz = buf.size(); // siz = 0
 bool at_end = buf.eos(); // at_end = true


3.2.3  CONSTRUCTION FROM VECTOR

 template <class item_type>
 bytefluo bytefluo_from_vector(
    const std::vector<item_type> & vec,
    bytefluo::byte_order bo)

This free function is provided as a convenient shorthand for
bytefluo(&vec[0], &vec[0] + vec.size(), bo) for a non-empty vector
and bytefluo(0, 0, bo) for an empty vector.

NOTE that any operations on the vector that might change the value
of &vec[0] or vec.size() (e.g. adding elements to the vector may
cause the vector to reallocate its buffer) will silently invalidate
the associated bytefluo object so that attempts to read the vector
contents via that bytefluo object may cause a CRASH.

Example:
 std::vector<uint8_t> vec(99);
 bytefluo buf(bytefluo_from_vector(vec, bytefluo::big));


3.2.4  SET DATA RANGE

 bytefluo & set_data_range(const void * begin, const void * end)

The bytefluo object will manage access to the bytes in the half open
range [begin, end). Throws bytefluo_exception if begin > end.

The cursor is set to the beginning of the range. The current
byte-order setting is unaffected. Returns *this.

Example:
 bytefluo buf;
 size_t siz = buf.size(); // siz = 0
 uint8_t foo[99];
 buf.set_data_range(foo, foo + sizeof(foo));
 siz = buf.size(); // siz = 99


3.2.5  SET BYTE ORDER

 bytefluo & set_byte_order(byte_order bo)

Specify the byte arrangement to be used on subsequent scalar reads.
'bo' must be one of

 bytefluo::big     most-significant byte has lowest address
 bytefluo::little  least-significant byte has lowest address

Throws bytefluo_exception if 'bo' is neither big nor little. The
current data range and cursor position are unaffected. Returns *this.

Example:
 bytefluo buf(...);
 buf.set_byte_order(bytefluo::big);


3.2.6  READ INTEGER SCALAR VIA operator>>()

 template <typename scalar_type>
 bytefluo & operator>>(scalar_type & out)

Read an integer scalar value from buffer at current cursor position.
The scalar is read assuming the byte order set at construction or
at the last call to set_byte_order(). [Cf. read_le() and read_be().]
The cursor is advanced by the size of the scalar read. Returns *this.

Throws bytefluo_exception if the read would move the cursor after
the end of the managed data range.

Example:
 bytefluo buf(...);
 uint16_t foo, bar;
 buf >> foo >> bar;  // read two successive shorts, foo followed by bar


3.2.7  READ INTEGER SCALAR VIA read_be() AND read_le() FUNCTIONS

 template <typename scalar_type>
 bytefluo & read_be(scalar_type & out)

 template <typename scalar_type>
 bytefluo & read_le(scalar_type & out)

Read an integer scalar value from buffer at current cursor position.
The scalar is read assuming big-endian byte order for read_be() and
little-endian byte order for read_le(), *regardless* of the byte order
set at construction or at the last call to set_byte_order(). [Cf.
operator>>().] The cursor is advanced by the size of the scalar read.
Returns *this.

Throws bytefluo_exception if the read would move the cursor after
the end of the managed data range.

Example:
 bytefluo buf(...);
 uint16_t foo, bar;
 // read big-endian foo followed by little-endian bar
 buf.read_be(foo).read_le(bar);


3.2.8  READ ARBITRARY NUMBER OF BYTES

 bytefluo & read(void * dest, size_t len)

Copy 'len' bytes from buffer at current cursor position to given
'dest' location. The cursor is advanced by the number of bytes
copied. The current byte order setting has no affect on this
operation. Returns *this.

Throws bytefluo_exception if the read would move the cursor after
the end of the managed data range.

Example:
 bytefluo buf(...);
 uint8_t foo[23];
 buf.read(foo, sizeof(foo));


3.2.9  MOVE THE CURSOR

 size_t seek_begin(size_t pos)
 size_t seek_current(long pos)
 size_t seek_end(size_t pos)

These functions move the cursor 'pos' bytes from stream beginning,
the current cursor location and the stream end respectively. They
all return the distance from buffer start to new cursor location.
seek_begin() and seek_end() require pos to be positive; seek_current()
may have a positive (move cursor toward end) or negative (move cursor
toward begin) actual parameter.

Throws bytefluo_exception if the move would put the cursor before
the beginning or after the end of the managed data range.

Example:
 bytefluo buf(...);
 size_t pos = buf.seek_end(3);  // cursor is 3 bytes from buffer end


3.2.10  TEST FOR END OF STREAM

 bool eos() const

Returns true if and only if the cursor is at the end of the stream.
Throws nothing.

Example:
 bytefluo buf(...);
 buf.seek_end(0);
 bool at_end = buf.eos();  // at_end = true


3.2.11  GET STREAM SIZE

 size_t size() const

Returns the number of bytes in the stream. Throws nothing.

Example:
 std::vector<unsigned char> v(99);
 bytefluo buf(bytefluo_from_vector(v, bytefluo::big));
 size_t siz = buf.size(); // siz = 99


3.2.12  GET CURSOR POSITION

 size_t tellg() const

Returns the distance from the buffer start to the current cursor
location. Throws nothing.

Example:
 bytefluo buf(...);
 buf.seek_begin(13);
 buf.seek_current(-3);
 size_t pos = buf.tellg();  // pos = 10


3.2.13 THE EXCEPTIONS

If something goes wrong the bytefluo object will throw a bytefluo_exception
object. You can catch this via the exception base class with

 catch (const std::runtime_error & e) {
   std::clog << e.what() << '\n';
 }

or directly with

 catch (const bytefluo_exception & e) {
   std::clog << e.what() << '\n';
 }

If you do the latter you can get access to the bytefluo exception id,
for example:

 catch (const bytefluo_exception & e) {
   std::clog << e.what() << '\n';
   if (e.id() == bytefluo_exception::attempt_to_read_past_end)
     . . .
 }

The exception ids and their symbolic names are

 1 null_begin_non_null_end
 2 null_end_non_null_begin
 3 end_precedes_begin
 4 invalid_byte_order
 5 attempt_to_read_past_end
 6 attempt_to_seek_after_end
 7 attempt_to_seek_before_beginning


4  LICENSE
 
 Distributed under the MIT License:

    MIT License

    Copyright (c) 2008,2009,2010,2016,2017 Anthony C. Hay

    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.

5  AUTHOR

 Anthony C. Hay
 March 2017
 anthony.hay.1@gmail.com

About

bytefluo is a C++ class for reading scalar values with specific byte order from byte streams

License:MIT License


Languages

Language:C++ 99.2%Language:Makefile 0.8%