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 size_t Size() const { return m_size; }
51
52 void Seek( size_t aPos )
53 {
54 if( aPos > m_size )
55 {
56 THROW_IO_ERROR( wxString::Format(
57 "Seek past end of file: offset %zu exceeds file size %zu", aPos, m_size ) );
58 }
59
60 m_pos = aPos;
61 }
62
63 void Skip( size_t aBytes )
64 {
65 if( aBytes > m_size - m_pos )
66 {
67 THROW_IO_ERROR( wxString::Format(
68 "Skip past end of file at offset %zu", m_pos ) );
69 }
70
71 m_pos += aBytes;
72 }
73
74 bool Eof() const { return m_pos >= m_size; }
75
82 void ReadBytes( void* aDest, size_t aSize )
83 {
84 if( aSize > m_size || m_pos > m_size - aSize )
85 {
86 THROW_IO_ERROR( wxString::Format(
87 "Failed to read requested %zu bytes at offset %zu (file size %zu)",
88 aSize, m_pos, m_size ) );
89 }
90
91 std::memcpy( aDest, m_data + m_pos, aSize );
92 m_pos += aSize;
93 }
94
95 // Primitive type reading (file is little-endian)
96 template <typename T>
98 {
99 static_assert( std::is_fundamental_v<T>, "Read() only works for fundamental types" );
100
101 T value;
102 ReadBytes( &value, sizeof( T ) );
103
104 if constexpr( ( sizeof( T ) > 1 ) && m_kNeedsSwap )
105 {
106 swapBytes( value );
107 }
108
109 return value;
110 }
111
112 std::string ReadString( bool aRoundToNextU32 )
113 {
114 if( m_pos > m_size )
115 {
116 THROW_IO_ERROR( wxString::Format(
117 "ReadString at invalid offset %zu (file size %zu)", m_pos, m_size ) );
118 }
119
120 const void* found = std::memchr( m_data + m_pos, '\0', m_size - m_pos );
121
122 if( !found )
123 {
124 THROW_IO_ERROR( wxString::Format(
125 "Unterminated string at offset %zu", m_pos ) );
126 }
127
128 const uint8_t* end = static_cast<const uint8_t*>( found );
129 std::string str( reinterpret_cast<const char*>( m_data + m_pos ), end - ( m_data + m_pos ) );
130 m_pos = static_cast<size_t>( end - m_data ) + 1;
131
132 if( aRoundToNextU32 && m_pos % sizeof( uint32_t ) != 0 )
133 {
134 const size_t padding = sizeof( uint32_t ) - ( m_pos % sizeof( uint32_t ) );
135 Skip( padding );
136 }
137
138 return str;
139 }
140
141 std::string ReadStringFixed( size_t aLen, bool aRoundToNextU32 )
142 {
143 if( aLen > m_size || m_pos > m_size - aLen )
144 {
145 THROW_IO_ERROR( wxString::Format(
146 "Failed to read fixed string of %zu bytes at offset %zu (file size %zu)",
147 aLen, m_pos, m_size ) );
148 }
149
150 const char* start = reinterpret_cast<const char*>( m_data + m_pos );
151 size_t strLen = strnlen( start, aLen );
152 std::string str( start, strLen );
153 m_pos += aLen;
154
155 if( aRoundToNextU32 && m_pos % sizeof( uint32_t ) != 0 )
156 {
157 const size_t padding = sizeof( uint32_t ) - ( m_pos % sizeof( uint32_t ) );
158 Skip( padding );
159 }
160
161 return str;
162 }
163
164 // Read a single byte from the stream, returning false if the stream is at EOF
165 bool GetU8( uint8_t& value )
166 {
167 if( m_pos >= m_size )
168 return false;
169
170 value = m_data[m_pos++];
171 return true;
172 }
173
174 uint8_t ReadU8() { return Read<uint8_t>(); }
175 uint16_t ReadU16() { return Read<uint16_t>(); }
176 int16_t ReadS16() { return Read<int16_t>(); }
177 uint32_t ReadU32() { return Read<uint32_t>(); }
178 int32_t ReadS32() { return Read<int32_t>(); }
179
180 void SkipU32( size_t n = 1 ) { Skip( sizeof( uint32_t ) * n ); }
181
182private:
183 template <typename T>
184 static void swapBytes( T& value )
185 {
186 char* bytes = reinterpret_cast<char*>( &value );
187 std::reverse( bytes, bytes + sizeof( T ) );
188 }
189
190 const uint8_t* m_data;
191 size_t m_size;
192 size_t m_pos;
193};
194
195} // namespace ALLEGRO
size_t Size() const
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