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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#pragma once
26
27#include <string>
28#include <bit>
29#include <cstdint>
30#include <cstring>
31#include <algorithm>
32
33#include <ki_exception.h>
35
36namespace ALLEGRO
37{
38
44{
45 static constexpr bool m_kNeedsSwap = std::endian::native != std::endian::little;
46
47public:
48 FILE_STREAM( const uint8_t* aData, size_t aSize ) :
49 m_data( aData ), m_size( aSize ), m_pos( 0 )
50 {}
51
52 size_t Position() const { return m_pos; }
53
54 void Seek( size_t aPos )
55 {
56 if( aPos > m_size )
57 {
58 THROW_IO_ERROR( wxString::Format(
59 "Seek past end of file: offset %zu exceeds file size %zu", aPos, m_size ) );
60 }
61
62 m_pos = aPos;
63 }
64
65 void Skip( size_t aBytes )
66 {
67 if( aBytes > m_size - m_pos )
68 {
69 THROW_IO_ERROR( wxString::Format(
70 "Skip past end of file at offset %zu", m_pos ) );
71 }
72
73 m_pos += aBytes;
74 }
75
76 bool Eof() const { return m_pos >= m_size; }
77
84 void ReadBytes( void* aDest, size_t aSize )
85 {
86 if( aSize > m_size || m_pos > m_size - aSize )
87 {
88 THROW_IO_ERROR( wxString::Format(
89 "Failed to read requested %zu bytes at offset %zu (file size %zu)",
90 aSize, m_pos, m_size ) );
91 }
92
93 std::memcpy( aDest, m_data + m_pos, aSize );
94 m_pos += aSize;
95 }
96
97 // Primitive type reading (file is little-endian)
98 template <typename T>
100 {
101 static_assert( std::is_fundamental_v<T>, "Read() only works for fundamental types" );
102
103 T value;
104 ReadBytes( &value, sizeof( T ) );
105
106 if constexpr( ( sizeof( T ) > 1 ) && m_kNeedsSwap )
107 {
108 swapBytes( value );
109 }
110
111 return value;
112 }
113
114 std::string ReadString( bool aRoundToNextU32 )
115 {
116 if( m_pos > m_size )
117 {
118 THROW_IO_ERROR( wxString::Format(
119 "ReadString at invalid offset %zu (file size %zu)", m_pos, m_size ) );
120 }
121
122 const void* found = std::memchr( m_data + m_pos, '\0', m_size - m_pos );
123
124 if( !found )
125 {
126 THROW_IO_ERROR( wxString::Format(
127 "Unterminated string at offset %zu", m_pos ) );
128 }
129
130 const uint8_t* end = static_cast<const uint8_t*>( found );
131 std::string str( reinterpret_cast<const char*>( m_data + m_pos ), end - ( m_data + m_pos ) );
132 m_pos = static_cast<size_t>( end - m_data ) + 1;
133
134 if( aRoundToNextU32 && m_pos % sizeof( uint32_t ) != 0 )
135 {
136 const size_t padding = sizeof( uint32_t ) - ( m_pos % sizeof( uint32_t ) );
137 Skip( padding );
138 }
139
140 return str;
141 }
142
143 std::string ReadStringFixed( size_t aLen, bool aRoundToNextU32 )
144 {
145 if( aLen > m_size || m_pos > m_size - aLen )
146 {
147 THROW_IO_ERROR( wxString::Format(
148 "Failed to read fixed string of %zu bytes at offset %zu (file size %zu)",
149 aLen, m_pos, m_size ) );
150 }
151
152 const char* start = reinterpret_cast<const char*>( m_data + m_pos );
153 size_t strLen = strnlen( start, aLen );
154 std::string str( start, strLen );
155 m_pos += aLen;
156
157 if( aRoundToNextU32 && m_pos % sizeof( uint32_t ) != 0 )
158 {
159 const size_t padding = sizeof( uint32_t ) - ( m_pos % sizeof( uint32_t ) );
160 Skip( padding );
161 }
162
163 return str;
164 }
165
166 // Read a single byte from the stream, returning false if the stream is at EOF
167 bool GetU8( uint8_t& value )
168 {
169 if( m_pos >= m_size )
170 return false;
171
172 value = m_data[m_pos++];
173 return true;
174 }
175
176 uint8_t ReadU8() { return Read<uint8_t>(); }
177 uint16_t ReadU16() { return Read<uint16_t>(); }
178 int16_t ReadS16() { return Read<int16_t>(); }
179 uint32_t ReadU32() { return Read<uint32_t>(); }
180 int32_t ReadS32() { return Read<int32_t>(); }
181
182 void SkipU32( size_t n = 1 ) { Skip( sizeof( uint32_t ) * n ); }
183
184private:
185 template <typename T>
186 static void swapBytes( T& value )
187 {
188 char* bytes = reinterpret_cast<char*>( &value );
189 std::reverse( bytes, bytes + sizeof( T ) );
190 }
191
192 const uint8_t* m_data;
193 size_t m_size;
194 size_t m_pos;
195};
196
197} // 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