26#include <boost/algorithm/string/split.hpp>
34std::string payload(
const std::vector<char>& aData )
36 if( aData.size() < 4 )
40 uint32_t length = stream.ReadU32();
42 if( length != aData.size() - 4 )
45 return std::string( aData.begin() + 4, aData.end() );
49std::vector<std::string>
split(
const std::string& aText,
unsigned char aSeparator )
51 std::vector<std::string>
result;
52 boost::split(
result, aText,
53 [aSeparator](
unsigned char aByte ) {
return aByte == aSeparator; } );
59uint32_t decimal(
const std::string& aText,
const wxString& aWhat )
65 auto [
end, error] = std::from_chars( aText.data(), aText.data() + aText.size(), value );
67 if( error != std::errc() ||
end != aText.data() + aText.size() )
74std::vector<std::string> readStrings(
ORCAD_STREAM& aStream,
const wxString& aWhat )
76 uint32_t count = aStream.
ReadU32();
77 std::vector<std::string>
result;
84 for( uint32_t i = 0; i < count; ++i )
95 std::string contents = payload( aData );
97 if( contents.size() == 1 &&
static_cast<unsigned char>( contents.front() ) == 0xFB )
100 std::vector<std::string> fields =
split( contents, aSeparator );
102 uint32_t count = decimal( fields.front(), wxS(
"OrCAD CIS list count is invalid" ) );
103 fields.erase( fields.begin() );
105 if( fields.size() != count )
106 THROW_IO_ERROR( wxS(
"OrCAD CIS list count does not match its contents" ) );
114 std::map<uint32_t, ORCAD_CIS_PROPERTIES>
result;
115 std::string
text = payload( aData );
116 constexpr char namesMark =
static_cast<char>( 0xB0 );
117 constexpr char valuesMark =
static_cast<char>( 0xC0 );
120 auto closesRecord = [&](
size_t aTilde )
122 if( aTilde + 1 ==
text.size() )
125 size_t digits = aTilde + 1;
127 while( digits <
text.size() && std::isdigit(
static_cast<unsigned char>(
text[digits] ) ) )
130 if( digits == aTilde + 1 || digits >=
text.size() ||
text[digits] != namesMark )
133 size_t values =
text.find( valuesMark, digits );
134 return values != std::string::npos &&
text.find(
'~', digits ) > values;
139 while( start <
text.size() )
141 size_t namesAt =
text.find( namesMark, start );
142 size_t valuesAt = namesAt == std::string::npos ? std::string::npos :
text.find( valuesMark, namesAt + 1 );
144 if( valuesAt == std::string::npos )
145 THROW_IO_ERROR( wxS(
"OrCAD CIS property update is malformed" ) );
147 size_t end =
text.find(
'~', valuesAt );
149 while(
end != std::string::npos && !closesRecord(
end ) )
152 uint32_t occurrence =
153 decimal(
text.substr( start, namesAt - start ), wxS(
"OrCAD CIS occurrence ID is invalid" ) );
154 std::vector<std::string> names =
split(
text.substr( namesAt + 1, valuesAt - namesAt - 1 ),
'^' );
155 std::vector<std::string> values =
156 split(
text.substr( valuesAt + 1,
end == std::string::npos ?
end :
end - valuesAt - 1 ),
'^' );
158 start =
end == std::string::npos ?
text.size() :
end + 1;
161 if( names.size() != values.size() )
163 if( start <
text.size() )
164 THROW_IO_ERROR( wxS(
"OrCAD CIS property names and values have different counts" ) );
169 auto& properties =
result[occurrence];
171 for(
size_t i = 0; i < names.size(); ++i )
173 if( names[i].
empty() )
176 properties[names[i]] = values[i];
186 std::map<uint32_t, bool>
result;
187 std::vector<std::string> records =
split( payload( aData ), 0xB0 );
190 for(
size_t i = 1; i < records.size(); ++i )
192 const std::string& record = records[i];
197 size_t separator = record.find(
'~' );
199 if( separator == std::string::npos || separator + 2 != record.size()
200 || ( record.back() !=
'0' && record.back() !=
'1' ) )
202 THROW_IO_ERROR( wxS(
"OrCAD CIS group membership is malformed" ) );
205 uint32_t occurrence =
206 decimal( record.substr( 0, separator ), wxS(
"OrCAD CIS membership occurrence ID is invalid" ) );
207 result[occurrence] = record.back() ==
'1';
218 uint32_t recordCount = stream.
ReadU32();
220 for( uint32_t recordIndex = 0; recordIndex < recordCount; ++recordIndex )
222 uint32_t databaseId = stream.
ReadU32();
223 std::vector<std::string> groups = readStrings( stream, wxS(
"OrCAD CIS group list is invalid" ) );
224 std::vector<std::string> states = readStrings( stream, wxS(
"OrCAD CIS group state list is invalid" ) );
225 uint32_t propertySetCount = stream.
ReadU32();
227 if( states.size() != groups.size() || propertySetCount != groups.size() )
228 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic group counts do not match" ) );
230 for( uint32_t groupIndex = 0; groupIndex < propertySetCount; ++groupIndex )
232 std::vector<std::string> names =
233 readStrings( stream, wxS(
"OrCAD CIS schematic property names are invalid" ) );
234 std::vector<std::string> values =
235 readStrings( stream, wxS(
"OrCAD CIS schematic property values are invalid" ) );
237 if( names.size() != values.size() )
238 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic property counts do not match" ) );
242 for(
size_t propertyIndex = 0; propertyIndex < names.size(); ++propertyIndex )
244 if( names[propertyIndex].
empty() )
245 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic property name is empty" ) );
247 properties[names[propertyIndex]] = values[propertyIndex];
254 if( !stream.
AtEnd() )
255 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic stream has trailing data" ) );
262 const std::optional<std::string>& aRequested )
266 if( std::find( aNames.begin(), aNames.end(), *aRequested ) == aNames.end() )
267 THROW_IO_ERROR( wxS(
"The requested OrCAD CIS variant does not exist" ) );
275 return *std::min_element( aNames.begin(), aNames.end() );
The caller owns the buffer.
size_t Remaining() const
Bytes left; 0 when the cursor is at or past the end.
std::string ReadLzt()
– strings ----------------------------------------------------------------—
static bool empty(const wxTextEntryBase *aCtrl)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
std::map< uint32_t, ORCAD_CIS_PROPERTIES > OrcadCisParsePropertyUpdates(const std::vector< char > &aData)
Occurrence id -> property overrides.
std::map< uint32_t, bool > OrcadCisParseMemberships(const std::vector< char > &aData)
Occurrence id -> installed state of each part in a group.
std::string OrcadCisSelectVariant(const std::vector< std::string > &aNames, const std::optional< std::string > &aRequested)
ORCAD_CIS_SCHEMATIC_INFO OrcadCisParseSchematicInfo(const std::vector< char > &aData)
std::vector< std::string > OrcadCisParseCountedList(const std::vector< char > &aData, uint8_t aSeparator)
std::map< std::string, std::map< uint32_t, ORCAD_CIS_PROPERTIES > > ORCAD_CIS_SCHEMATIC_INFO
std::map< std::string, std::string > ORCAD_CIS_PROPERTIES
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
wxString result
Test unit parsing edge cases and error handling.