KiCad PCB EDA Suite
Loading...
Searching...
No Matches
allegro_stream.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright Quilter
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#include <string>
24#include <bit>
25#include <cstdint>
26#include <cstring>
27#include <algorithm>
28
29#include <ki_exception.h>
31
32namespace ALLEGRO
33{
34
40{
41 static constexpr bool m_kNeedsSwap = std::endian::native != std::endian::little;
42
43public:
44 FILE_STREAM( const uint8_t* aData, size_t aSize ) :
45 m_data( aData ), m_size( aSize ), m_pos( 0 )
46 {}
47
48 size_t Position() const { return m_pos; }
49
50 void Seek( size_t aPos )
51 {
52 if( aPos > m_size )
53 {
54 THROW_IO_ERROR( wxString::Format(
55 "Seek past end of file: offset %zu exceeds file size %zu", aPos, m_size ) );
56 }
57
58 m_pos = aPos;
59 }
60
61 void Skip( size_t aBytes )
62 {
63 if( aBytes > m_size - m_pos )
64 {
65 THROW_IO_ERROR( wxString::Format(
66 "Skip past end of file at offset %zu", m_pos ) );
67 }
68
69 m_pos += aBytes;
70 }
71
72 bool Eof() const { return m_pos >= m_size; }
73
80 void ReadBytes( void* aDest, size_t aSize )
81 {
82 if( aSize > m_size || m_pos > m_size - aSize )
83 {
84 THROW_IO_ERROR( wxString::Format(
85 "Failed to read requested %zu bytes at offset %zu (file size %zu)",
86 aSize, m_pos, m_size ) );
87 }
88
89 std::memcpy( aDest, m_data + m_pos, aSize );
90 m_pos += aSize;
91 }
92
93 // Primitive type reading (file is little-endian)
94 template <typename T>
96 {
97 static_assert( std::is_fundamental_v<T>, "Read() only works for fundamental types" );
98
99 T value;
100 ReadBytes( &value, sizeof( T ) );
101
102 if constexpr( ( sizeof( T ) > 1 ) && m_kNeedsSwap )
103 {
104 swapBytes( value );
105 }
106
107 return value;
108 }
109
110 std::string ReadString( bool aRoundToNextU32 )
111 {
112 if( m_pos > m_size )
113 {
114 THROW_IO_ERROR( wxString::Format(
115 "ReadString at invalid offset %zu (file size %zu)", m_pos, m_size ) );
116 }
117
118 const void* found = std::memchr( m_data + m_pos, '\0', m_size - m_pos );
119
120 if( !found )
121 {
122 THROW_IO_ERROR( wxString::Format(
123 "Unterminated string at offset %zu", m_pos ) );
124 }
125
126 const uint8_t* end = static_cast<const uint8_t*>( found );
127 std::string str( reinterpret_cast<const char*>( m_data + m_pos ), end - ( m_data + m_pos ) );
128 m_pos = static_cast<size_t>( end - m_data ) + 1;
129
130 if( aRoundToNextU32 && m_pos % sizeof( uint32_t ) != 0 )
131 {
132 const size_t padding = sizeof( uint32_t ) - ( m_pos % sizeof( uint32_t ) );
133 Skip( padding );
134 }
135
136 return str;
137 }
138
139 std::string ReadStringFixed( size_t aLen, bool aRoundToNextU32 )
140 {
141 if( aLen > m_size || m_pos > m_size - aLen )
142 {
143 THROW_IO_ERROR( wxString::Format(
144 "Failed to read fixed string of %zu bytes at offset %zu (file size %zu)",
145 aLen, m_pos, m_size ) );
146 }
147
148 const char* start = reinterpret_cast<const char*>( m_data + m_pos );
149 size_t strLen = strnlen( start, aLen );
150 std::string str( start, strLen );
151 m_pos += aLen;
152
153 if( aRoundToNextU32 && m_pos % sizeof( uint32_t ) != 0 )
154 {
155 const size_t padding = sizeof( uint32_t ) - ( m_pos % sizeof( uint32_t ) );
156 Skip( padding );
157 }
158
159 return str;
160 }
161
162 // Read a single byte from the stream, returning false if the stream is at EOF
163 bool GetU8( uint8_t& value )
164 {
165 if( m_pos >= m_size )
166 return false;
167
168 value = m_data[m_pos++];
169 return true;
170 }
171
172 uint8_t ReadU8() { return Read<uint8_t>(); }
173 uint16_t ReadU16() { return Read<uint16_t>(); }
174 int16_t ReadS16() { return Read<int16_t>(); }
175 uint32_t ReadU32() { return Read<uint32_t>(); }
176 int32_t ReadS32() { return Read<int32_t>(); }
177
178 void SkipU32( size_t n = 1 ) { Skip( sizeof( uint32_t ) * n ); }
179
180private:
181 template <typename T>
182 static void swapBytes( T& value )
183 {
184 char* bytes = reinterpret_cast<char*>( &value );
185 std::reverse( bytes, bytes + sizeof( T ) );
186 }
187
188 const uint8_t* m_data;
189 size_t m_size;
190 size_t m_pos;
191};
192
193} // namespace ALLEGRO
const uint8_t * m_data
static constexpr bool m_kNeedsSwap
void ReadBytes(void *aDest, size_t aSize)
Read a number of bytes from the stream into the destination buffer.
void Skip(size_t aBytes)
void SkipU32(size_t n=1)
size_t Position() const
FILE_STREAM(const uint8_t *aData, size_t aSize)
std::string ReadString(bool aRoundToNextU32)
bool GetU8(uint8_t &value)
static void swapBytes(T &value)
void Seek(size_t aPos)
std::string ReadStringFixed(size_t aLen, bool aRoundToNextU32)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
VECTOR2I end