// The MIT License (MIT) // Simplistic Binary Streams 1.0.3 // Copyright (C) 2014 - 2019, by Wong Shao Voon (shaovoon@yahoo.com) // // http://opensource.org/licenses/MIT // // version 0.9.2 : Optimize mem_istream constructor for const char* // version 0.9.3 : Optimize mem_ostream vector insert // version 0.9.4 : New ptr_istream class // version 0.9.5 : Add Endianness Swap with compile time check // version 0.9.6 : Using C File APIs, instead of STL file streams // version 0.9.7 : Add memfile_istream // version 0.9.8 : Fix GCC and Clang template errors // version 0.9.9 : Fix bug of getting previous value when reading empty string // version 1.0.0 : Fix buffer overrun bug when reading string (reported by imtrobin) // version 1.0.1 : Fix memfile_istream tellg and seekg bug reported by macxfadz, // use is_arithmetic instead of is_integral to determine swapping // version 1.0.2 : Add overloaded open functions that take in file parameter in // wide char type.(only available on win32) // version 1.0.3 : Remove header // version 1.0.4 : Fixed file_istream's seekg() and added writeat() to mem_ostream and memfile_ostream. Thanks Festering from CodeProject. #ifndef SimpleBinStream_H #define SimpleBinStream_H #include #include #include #include #include #include #include namespace nczarr { enum class Endian { Big, Little }; using BigEndian = std::integral_constant; using LittleEndian = std::integral_constant; struct SizeOf1 { }; struct SizeOf2 { }; struct SizeOf4 { }; struct SizeOf8 { }; struct UnknownSize { }; template void swap_endian(T& ui, UnknownSize) { } template void swap_endian(T& ui, SizeOf1) { } template void swap_endian(T& ui, SizeOf8) { union EightBytes { T ui; uint8_t arr[8]; }; EightBytes fb; fb.ui = ui; // swap the endian std::swap(fb.arr[0], fb.arr[7]); std::swap(fb.arr[1], fb.arr[6]); std::swap(fb.arr[2], fb.arr[5]); std::swap(fb.arr[3], fb.arr[4]); ui = fb.ui; } template void swap_endian(T& ui, SizeOf4) { union FourBytes { T ui; uint8_t arr[4]; }; FourBytes fb; fb.ui = ui; // swap the endian std::swap(fb.arr[0], fb.arr[3]); std::swap(fb.arr[1], fb.arr[2]); ui = fb.ui; } template void swap_endian(T& ui, SizeOf2) { union TwoBytes { T ui; uint8_t arr[2]; }; TwoBytes fb; fb.ui = ui; // swap the endian std::swap(fb.arr[0], fb.arr[1]); ui = fb.ui; } template using number_type = typename std::conditional< sizeof(T) == 1, SizeOf1, typename std::conditional< sizeof(T) == 2, SizeOf2, typename std::conditional< sizeof(T) == 4, SizeOf4, typename std::conditional< sizeof(T) == 8, SizeOf8, UnknownSize >::type >::type >::type >::type; template void swap_if_arithmetic(T& val, std::true_type) { swap_endian(val, number_type()); } template void swap_if_arithmetic(T& val, std::false_type) { // T is not arithmetic so do nothing } template void swap_endian_if_same_endian_is_false(T& val, std::false_type) { std::is_arithmetic is_integral_type; swap_if_arithmetic(val, is_integral_type); } template void swap_endian_if_same_endian_is_false(T& val, std::true_type) { // same endian so do nothing. } template class file_istream { public: file_istream() : input_file_ptr(nullptr), file_size(0L), read_length(0L) {} file_istream(const char * file) : input_file_ptr(nullptr), file_size(0L), read_length(0L) { open(file); } #ifdef _MSC_VER file_istream(const wchar_t * file) : input_file_ptr(nullptr), file_size(0L), read_length(0L) { open(file); } #endif ~file_istream() { close(); } void open(const char * file) { close(); #ifdef _MSC_VER input_file_ptr = nullptr; fopen_s(&input_file_ptr, file, "rb"); #else input_file_ptr = std::fopen(file, "rb"); #endif compute_length(); } #ifdef _MSC_VER void open(const wchar_t * file) { close(); input_file_ptr = nullptr; _wfopen_s(&input_file_ptr, file, L"rb"); compute_length(); } #endif void close() { if (input_file_ptr) { fclose(input_file_ptr); input_file_ptr = nullptr; } } bool is_open() { return (input_file_ptr != nullptr); } long file_length() const { return file_size; } // http://www.cplusplus.com/reference/cstdio/feof/ // stream's internal position indicator may point to the end-of-file for the // next operation, but still, the end-of-file indicator may not be set until // an operation attempts to read at that point. bool eof() const // not using feof(), see above { return read_length >= file_size; } long tellg() const { return std::ftell(input_file_ptr); } void seekg (long pos) { std::fseek(input_file_ptr, pos, SEEK_SET); read_length = pos; } void seekg (long offset, int way) { std::fseek(input_file_ptr, offset, way); if (way == SEEK_END) read_length = file_size - offset; else if (way == SEEK_CUR) read_length += offset; else read_length = offset; } template void read(T& t) { if(std::fread(reinterpret_cast(&t), sizeof(T), 1, input_file_ptr) != 1) { throw std::runtime_error("Read Error!"); } read_length += sizeof(T); nczarr::swap_endian_if_same_endian_is_false(t, m_same_type); } void read(typename std::vector& vec) { if (std::fread(reinterpret_cast(&vec[0]), vec.size(), 1, input_file_ptr) != 1) { throw std::runtime_error("Read Error!"); } read_length += vec.size(); } void read(char* p, size_t size) { if (std::fread(reinterpret_cast(p), size, 1, input_file_ptr) != 1) { throw std::runtime_error("Read Error!"); } read_length += size; } private: void compute_length() { seekg(0, SEEK_END); file_size = tellg(); seekg(0, SEEK_SET); } std::FILE* input_file_ptr; long file_size; long read_length; same_endian_type m_same_type; }; template file_istream& operator >> ( file_istream& istm, T& val) { istm.read(val); return istm; } template file_istream& operator >> ( file_istream& istm, std::string& val) { val.clear(); int size = 0; istm.read(size); if(size<=0) return istm; std::vector vec((size_t)size); istm.read(vec); val.assign(&vec[0], (size_t)size); return istm; } template class mem_istream { public: mem_istream() : m_index(0) {} mem_istream(const char * mem, size_t size) { open(mem, size); } mem_istream(const std::vector& vec) { m_index = 0; m_vec.reserve(vec.size()); m_vec.assign(vec.begin(), vec.end()); } void open(const char * mem, size_t size) { m_index = 0; m_vec.clear(); m_vec.reserve(size); m_vec.assign(mem, mem + size); } void close() { m_vec.clear(); } bool eof() const { return m_index >= m_vec.size(); } std::ifstream::pos_type tellg() const { return m_index; } bool seekg (size_t pos) { if(pos& get_internal_vec() { return m_vec; } template void read(T& t) { if(eof()) throw std::runtime_error("Premature end of array!"); if((m_index + sizeof(T)) > m_vec.size()) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(&t), &m_vec[m_index], sizeof(T)); nczarr::swap_endian_if_same_endian_is_false(t, m_same_type); m_index += sizeof(T); } void read(typename std::vector& vec) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + vec.size()) > m_vec.size()) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(&vec[0]), &m_vec[m_index], vec.size()); m_index += vec.size(); } void read(char* p, size_t size) { if(eof()) throw std::runtime_error("Premature end of array!"); if((m_index + size) > m_vec.size()) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(p), &m_vec[m_index], size); m_index += size; } void read(std::string& str, const unsigned int size) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + size) > m_vec.size()) throw std::runtime_error("Premature end of array!"); str.assign(&m_vec[m_index], size); m_index += str.size(); } private: std::vector m_vec; size_t m_index; same_endian_type m_same_type; }; template mem_istream& operator >> ( mem_istream& istm, T& val) { istm.read(val); return istm; } template mem_istream& operator >> (mem_istream& istm, std::string& val) { val.clear(); int size = 0; istm.read(size); if(size<=0) return istm; istm.read(val, size); return istm; } template class ptr_istream { public: ptr_istream() : m_arr(nullptr), m_size(0), m_index(0) {} ptr_istream(const char * mem, size_t size) : m_arr(nullptr), m_size(0), m_index(0) { open(mem, size); } ptr_istream(const std::vector& vec) { m_index = 0; m_arr = vec.data(); m_size = vec.size(); } void open(const char * mem, size_t size) { m_index = 0; m_arr = mem; m_size = size; } void close() { m_arr = nullptr; m_size = 0; m_index = 0; } bool eof() const { return m_index >= m_size; } std::ifstream::pos_type tellg() const { return m_index; } bool seekg(size_t pos) { if (pos void read(T& t) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + sizeof(T)) > m_size) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(&t), &m_arr[m_index], sizeof(T)); nczarr::swap_endian_if_same_endian_is_false(t, m_same_type); m_index += sizeof(T); } void read(typename std::vector& vec) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + vec.size()) > m_size) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(&vec[0]), &m_arr[m_index], vec.size()); m_index += vec.size(); } void read(char* p, size_t size) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + size) > m_size) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(p), &m_arr[m_index], size); m_index += size; } void read(std::string& str, const unsigned int size) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + size) > m_size) throw std::runtime_error("Premature end of array!"); str.assign(&m_arr[m_index], size); m_index += str.size(); } private: const char* m_arr; size_t m_size; size_t m_index; same_endian_type m_same_type; }; template ptr_istream& operator >> ( ptr_istream& istm, T& val) { istm.read(val); return istm; } template ptr_istream& operator >> ( ptr_istream& istm, std::string& val) { val.clear(); int size = 0; istm.read(size); if (size <= 0) return istm; istm.read(val, size); return istm; } template class memfile_istream { public: memfile_istream() : m_arr(nullptr), m_size(0), m_index(0) {} memfile_istream(const char * file) : m_arr(nullptr), m_size(0), m_index(0) { open(file); } #ifdef _MSC_VER memfile_istream(const wchar_t * file) : m_arr(nullptr), m_size(0), m_index(0) { open(file); } #endif ~memfile_istream() { close(); } void open(const char * file) { close(); #ifdef _MSC_VER std::FILE* input_file_ptr = nullptr; fopen_s(&input_file_ptr, file, "rb"); #else std::FILE* input_file_ptr = std::fopen(file, "rb"); #endif compute_length(input_file_ptr); m_arr = new char[m_size]; std::fread(m_arr, m_size, 1, input_file_ptr); fclose(input_file_ptr); } #ifdef _MSC_VER void open(const wchar_t * file) { close(); std::FILE* input_file_ptr = nullptr; _wfopen_s(&input_file_ptr, file, L"rb"); compute_length(input_file_ptr); m_arr = new char[m_size]; std::fread(m_arr, m_size, 1, input_file_ptr); fclose(input_file_ptr); } #endif void close() { if (m_arr) { delete[] m_arr; m_arr = nullptr; m_size = 0; m_index = 0; } } bool is_open() { return (m_arr != nullptr); } long file_length() const { return m_size; } bool eof() const { return m_index >= m_size; } std::ifstream::pos_type tellg() const { return m_index; } bool seekg(size_t pos) { if (pos < m_size) m_index = pos; else return false; return true; } bool seekg(std::streamoff offset, std::ios_base::seekdir way) { if (way == std::ios_base::beg && offset < m_size) m_index = offset; else if (way == std::ios_base::cur && (m_index + offset) < m_size) m_index += offset; else if (way == std::ios_base::end && (m_size + offset) < m_size) m_index = m_size + offset; else return false; return true; } template void read(T& t) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + sizeof(T)) > m_size) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(&t), &m_arr[m_index], sizeof(T)); nczarr::swap_endian_if_same_endian_is_false(t, m_same_type); m_index += sizeof(T); } void read(typename std::vector& vec) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + vec.size()) > m_size) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(&vec[0]), &m_arr[m_index], vec.size()); m_index += vec.size(); } void read(char* p, size_t size) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + size) > m_size) throw std::runtime_error("Premature end of array!"); std::memcpy(reinterpret_cast(p), &m_arr[m_index], size); m_index += size; } void read(std::string& str, const unsigned int size) { if (eof()) throw std::runtime_error("Premature end of array!"); if ((m_index + size) > m_size) throw std::runtime_error("Premature end of array!"); str.assign(&m_arr[m_index], size); m_index += str.size(); } private: void compute_length(std::FILE* input_file_ptr) { std::fseek(input_file_ptr, 0, SEEK_END); m_size = std::ftell(input_file_ptr); std::fseek(input_file_ptr, 0, SEEK_SET); } char* m_arr; size_t m_size; size_t m_index; same_endian_type m_same_type; }; template memfile_istream& operator >> ( memfile_istream& istm, T& val) { istm.read(val); return istm; } template memfile_istream& operator >> ( memfile_istream& istm, std::string& val) { val.clear(); int size = 0; istm.read(size); if (size <= 0) return istm; istm.read(val, size); return istm; } template class file_ostream { public: file_ostream() : output_file_ptr(nullptr) {} file_ostream(const char * file) : output_file_ptr(nullptr) { open(file); } #ifdef _MSC_VER file_ostream(const wchar_t * file) : output_file_ptr(nullptr) { open(file); } #endif ~file_ostream() { close(); } void open(const char * file) { close(); #ifdef _MSC_VER output_file_ptr = nullptr; fopen_s(&output_file_ptr, file, "wb"); #else output_file_ptr = std::fopen(file, "wb"); #endif } #ifdef _MSC_VER void open(const wchar_t * file) { close(); output_file_ptr = nullptr; _wfopen_s(&output_file_ptr, file, L"wb"); } #endif void flush() { std::fflush(output_file_ptr); } void close() { if (output_file_ptr) { std::fclose(output_file_ptr); output_file_ptr = nullptr; } } bool is_open() { return output_file_ptr != nullptr; } template void write(const T& t) { T t2 = t; nczarr::swap_endian_if_same_endian_is_false(t2, m_same_type); std::fwrite(reinterpret_cast(&t2), sizeof(T), 1, output_file_ptr); } void write(const std::vector& vec) { std::fwrite(reinterpret_cast(&vec[0]), vec.size(), 1, output_file_ptr); } void write(const char* p, size_t size) { std::fwrite(reinterpret_cast(p), size, 1, output_file_ptr); } private: std::FILE* output_file_ptr; same_endian_type m_same_type; }; template file_ostream& operator << (file_ostream& ostm, const T& val) { ostm.write(val); return ostm; } template file_ostream& operator << ( file_ostream& ostm, const std::string& val) { int size = val.size(); ostm.write(size); if(val.size()<=0) return ostm; ostm.write(val.c_str(), val.size()); return ostm; } template file_ostream& operator << ( file_ostream& ostm, const char* val) { int size = std::strlen(val); ostm.write(size); if(size<=0) return ostm; ostm.write(val, size); return ostm; } template class mem_ostream { public: mem_ostream() {} void close() { m_vec.clear(); } const std::vector& get_internal_vec() { return m_vec; } template void write(const T& t) { std::vector vec(sizeof(T)); T t2 = t; nczarr::swap_endian_if_same_endian_is_false(t2, m_same_type); std::memcpy(reinterpret_cast(&vec[0]), reinterpret_cast(&t2), sizeof(T)); write(vec); } void write(const std::vector& vec) { m_vec.insert(m_vec.end(), vec.begin(), vec.end()); } void write(const char* p, size_t size) { for(size_t i=0; i void writeat(size_t pos, const T& t) { std::vector vec(sizeof(T)); T t2 = t; nczarr::swap_endian_if_same_endian_is_false(t2, m_same_type); std::memcpy(reinterpret_cast(&vec[0]), reinterpret_cast(&t2), sizeof(T)); writeat(pos, vec); } void writeat(size_t pos, const std::vector& vec) { for (size_t n = 0, count = vec.size(); n < count; n++) m_vec[pos++] = vec[n]; } private: std::vector m_vec; same_endian_type m_same_type; }; template mem_ostream& operator << ( mem_ostream& ostm, const T& val) { ostm.write(val); return ostm; } template mem_ostream& operator << ( mem_ostream& ostm, const std::string& val) { int size = val.size(); ostm.write(size); if(val.size()<=0) return ostm; ostm.write(val.c_str(), val.size()); return ostm; } template mem_ostream& operator << ( mem_ostream& ostm, const char* val) { int size = std::strlen(val); ostm.write(size); if(size<=0) return ostm; ostm.write(val, size); return ostm; } template class memfile_ostream { public: memfile_ostream() {} void close() { m_vec.clear(); } const std::vector& get_internal_vec() { return m_vec; } template void write(const T& t) { std::vector vec(sizeof(T)); T t2 = t; nczarr::swap_endian_if_same_endian_is_false(t2, m_same_type); std::memcpy(reinterpret_cast(&vec[0]), reinterpret_cast(&t2), sizeof(T)); write(vec); } void write(const std::vector& vec) { m_vec.insert(m_vec.end(), vec.begin(), vec.end()); } void write(const char* p, size_t size) { for (size_t i = 0; i void writeat(size_t pos, const T& t) { std::vector vec(sizeof(T)); T t2 = t; nczarr::swap_endian_if_same_endian_is_false(t2, m_same_type); std::memcpy(reinterpret_cast(&vec[0]), reinterpret_cast(&t2), sizeof(T)); writeat(pos, vec); } void writeat(size_t pos, const std::vector& vec) { for (size_t n = 0, count = vec.size(); n < count; n++) m_vec[pos++] = vec[n]; } bool write_to_file(const char* file) { #ifdef _MSC_VER std::FILE* fp = nullptr; fopen_s(&fp, file, "wb"); #else std::FILE* fp = std::fopen(file, "wb"); #endif if (fp) { size_t size = std::fwrite(m_vec.data(), m_vec.size(), 1, fp); std::fflush(fp); std::fclose(fp); m_vec.clear(); return size == 1u; } return false; } #ifdef _MSC_VER bool write_to_file(const wchar_t* file) { std::FILE* fp = nullptr; _wfopen_s(&fp, file, L"wb"); if (fp) { size_t size = std::fwrite(m_vec.data(), m_vec.size(), 1, fp); std::fflush(fp); std::fclose(fp); m_vec.clear(); return size == 1u; } return false; } #endif private: std::vector m_vec; same_endian_type m_same_type; }; template memfile_ostream& operator << ( memfile_ostream& ostm, const T& val) { ostm.write(val); return ostm; } template memfile_ostream& operator << ( memfile_ostream& ostm, const std::string& val) { int size = val.size(); ostm.write(size); if (val.size() <= 0) return ostm; ostm.write(val.c_str(), val.size()); return ostm; } template memfile_ostream& operator << ( memfile_ostream& ostm, const char* val) { int size = std::strlen(val); ostm.write(size); if (size <= 0) return ostm; ostm.write(val, size); return ostm; } } // ns simple #endif // SimpleBinStream_H