Merge in fluxfile stuff.

This commit is contained in:
David Given
2025-08-17 21:12:27 +02:00
25 changed files with 1076 additions and 187 deletions

View File

@@ -263,6 +263,11 @@ As an exception, `dep/lexy` contains a partial copy of the lexy package, written
by foonathen@github, taken from https://github.com/foonathan/lexy. It is BSL 1.0 by foonathen@github, taken from https://github.com/foonathan/lexy. It is BSL 1.0
licensed. Please see the contents of the directory for the full text. licensed. Please see the contents of the directory for the full text.
As an exception, `dep/alphanum` contains a copy of the alphanum package,
written by Dave Koelle, taken from
https://web.archive.org/web/20210207124255/davekoelle.com/alphanum.html. It is
MIT licensed. Please see the source for the full text.
__Important:__ Because of all these exceptions, if you distribute the __Important:__ Because of all these exceptions, if you distribute the
FluxEngine package as a whole, you must comply with the terms of _all_ of the FluxEngine package as a whole, you must comply with the terms of _all_ of the
licensing terms. This means that __effectively the FluxEngine package is licensing terms. This means that __effectively the FluxEngine package is

2
dep/alphanum/UPSTREAM.md Normal file
View File

@@ -0,0 +1,2 @@
Downloaded from:
https://web.archive.org/web/20210918044134/http://davekoelle.com/files/alphanum.hpp

450
dep/alphanum/alphanum.h Normal file
View File

@@ -0,0 +1,450 @@
#ifndef ALPHANUM__HPP
#define ALPHANUM__HPP
/*
Released under the MIT License - https://opensource.org/licenses/MIT
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* $Header: /code/doj/alphanum.hpp,v 1.3 2008/01/28 23:06:47 doj Exp $ */
#include <cassert>
#include <functional>
#include <string>
#include <sstream>
#ifdef ALPHANUM_LOCALE
#include <cctype>
#endif
#ifdef DOJDEBUG
#include <iostream>
#include <typeinfo>
#endif
// TODO: make comparison with hexadecimal numbers. Extend the alphanum_comp() function by traits to choose between decimal and hexadecimal.
namespace doj
{
// anonymous namespace for functions we use internally. But if you
// are coding in C, you can use alphanum_impl() directly, since it
// uses not C++ features.
namespace {
// if you want to honour the locale settings for detecting digit
// characters, you should define ALPHANUM_LOCALE
#ifdef ALPHANUM_LOCALE
/** wrapper function for ::isdigit() */
bool alphanum_isdigit(int c)
{
return isdigit(c);
}
#else
/** this function does not consider the current locale and only
works with ASCII digits.
@return true if c is a digit character
*/
bool alphanum_isdigit(const char c)
{
return c>='0' && c<='9';
}
#endif
/**
compare l and r with strcmp() semantics, but using
the "Alphanum Algorithm". This function is designed to read
through the l and r strings only one time, for
maximum performance. It does not allocate memory for
substrings. It can either use the C-library functions isdigit()
and atoi() to honour your locale settings, when recognizing
digit characters when you "#define ALPHANUM_LOCALE=1" or use
it's own digit character handling which only works with ASCII
digit characters, but provides better performance.
@param l NULL-terminated C-style string
@param r NULL-terminated C-style string
@return negative if l<r, 0 if l equals r, positive if l>r
*/
int alphanum_impl(const char *l, const char *r)
{
enum mode_t { STRING, NUMBER } mode=STRING;
while(*l && *r)
{
if(mode == STRING)
{
char l_char, r_char;
while((l_char=*l) && (r_char=*r))
{
// check if this are digit characters
const bool l_digit=alphanum_isdigit(l_char), r_digit=alphanum_isdigit(r_char);
// if both characters are digits, we continue in NUMBER mode
if(l_digit && r_digit)
{
mode=NUMBER;
break;
}
// if only the left character is a digit, we have a result
if(l_digit) return -1;
// if only the right character is a digit, we have a result
if(r_digit) return +1;
// compute the difference of both characters
const int diff=l_char - r_char;
// if they differ we have a result
if(diff != 0) return diff;
// otherwise process the next characters
++l;
++r;
}
}
else // mode==NUMBER
{
#ifdef ALPHANUM_LOCALE
// get the left number
char *end;
unsigned long l_int=strtoul(l, &end, 0);
l=end;
// get the right number
unsigned long r_int=strtoul(r, &end, 0);
r=end;
#else
// get the left number
unsigned long l_int=0;
while(*l && alphanum_isdigit(*l))
{
// TODO: this can overflow
l_int=l_int*10 + *l-'0';
++l;
}
// get the right number
unsigned long r_int=0;
while(*r && alphanum_isdigit(*r))
{
// TODO: this can overflow
r_int=r_int*10 + *r-'0';
++r;
}
#endif
// if the difference is not equal to zero, we have a comparison result
const long diff=l_int-r_int;
if(diff != 0)
return diff;
// otherwise we process the next substring in STRING mode
mode=STRING;
}
}
if(*r) return -1;
if(*l) return +1;
return 0;
}
}
/**
Compare left and right with the same semantics as strcmp(), but with the
"Alphanum Algorithm" which produces more human-friendly
results. The classes lT and rT must implement "std::ostream
operator<< (std::ostream&, const Ty&)".
@return negative if left<right, 0 if left==right, positive if left>right.
*/
template <typename lT, typename rT>
int alphanum_comp(const lT& left, const rT& right)
{
#ifdef DOJDEBUG
std::clog << "alphanum_comp<" << typeid(left).name() << "," << typeid(right).name() << "> " << left << "," << right << std::endl;
#endif
std::ostringstream l; l << left;
std::ostringstream r; r << right;
return alphanum_impl(l.str().c_str(), r.str().c_str());
}
/**
Compare l and r with the same semantics as strcmp(), but with
the "Alphanum Algorithm" which produces more human-friendly
results.
@return negative if l<r, 0 if l==r, positive if l>r.
*/
template <>
int alphanum_comp<std::string>(const std::string& l, const std::string& r)
{
#ifdef DOJDEBUG
std::clog << "alphanum_comp<std::string,std::string> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l.c_str(), r.c_str());
}
////////////////////////////////////////////////////////////////////////////
// now follow a lot of overloaded alphanum_comp() functions to get a
// direct call to alphanum_impl() upon the various combinations of c
// and c++ strings.
/**
Compare l and r with the same semantics as strcmp(), but with
the "Alphanum Algorithm" which produces more human-friendly
results.
@return negative if l<r, 0 if l==r, positive if l>r.
*/
int alphanum_comp(char* l, char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<char*,char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(const char* l, const char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<const char*,const char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(char* l, const char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<char*,const char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(const char* l, char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<const char*,char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(const std::string& l, char* r)
{
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<std::string,char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l.c_str(), r);
}
int alphanum_comp(char* l, const std::string& r)
{
assert(l);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<char*,std::string> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r.c_str());
}
int alphanum_comp(const std::string& l, const char* r)
{
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<std::string,const char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l.c_str(), r);
}
int alphanum_comp(const char* l, const std::string& r)
{
assert(l);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<const char*,std::string> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r.c_str());
}
////////////////////////////////////////////////////////////////////////////
/**
Functor class to compare two objects with the "Alphanum
Algorithm". If the objects are no std::string, they must
implement "std::ostream operator<< (std::ostream&, const Ty&)".
*/
template<class Ty>
struct alphanum_less
{
bool operator()(const Ty& left, const Ty& right) const
{
return alphanum_comp(left, right) < 0;
}
};
}
#ifdef TESTMAIN
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
#include <set>
#include <vector>
int main()
{
// testcases for the algorithm
assert(doj::alphanum_comp("","") == 0);
assert(doj::alphanum_comp("","a") < 0);
assert(doj::alphanum_comp("a","") > 0);
assert(doj::alphanum_comp("a","a") == 0);
assert(doj::alphanum_comp("","9") < 0);
assert(doj::alphanum_comp("9","") > 0);
assert(doj::alphanum_comp("1","1") == 0);
assert(doj::alphanum_comp("1","2") < 0);
assert(doj::alphanum_comp("3","2") > 0);
assert(doj::alphanum_comp("a1","a1") == 0);
assert(doj::alphanum_comp("a1","a2") < 0);
assert(doj::alphanum_comp("a2","a1") > 0);
assert(doj::alphanum_comp("a1a2","a1a3") < 0);
assert(doj::alphanum_comp("a1a2","a1a0") > 0);
assert(doj::alphanum_comp("134","122") > 0);
assert(doj::alphanum_comp("12a3","12a3") == 0);
assert(doj::alphanum_comp("12a1","12a0") > 0);
assert(doj::alphanum_comp("12a1","12a2") < 0);
assert(doj::alphanum_comp("a","aa") < 0);
assert(doj::alphanum_comp("aaa","aa") > 0);
assert(doj::alphanum_comp("Alpha 2","Alpha 2") == 0);
assert(doj::alphanum_comp("Alpha 2","Alpha 2A") < 0);
assert(doj::alphanum_comp("Alpha 2 B","Alpha 2") > 0);
assert(doj::alphanum_comp(1,1) == 0);
assert(doj::alphanum_comp(1,2) < 0);
assert(doj::alphanum_comp(2,1) > 0);
assert(doj::alphanum_comp(1.2,3.14) < 0);
assert(doj::alphanum_comp(3.14,2.71) > 0);
assert(doj::alphanum_comp(true,true) == 0);
assert(doj::alphanum_comp(true,false) > 0);
assert(doj::alphanum_comp(false,true) < 0);
std::string str("Alpha 2");
assert(doj::alphanum_comp(str,"Alpha 2") == 0);
assert(doj::alphanum_comp(str,"Alpha 2A") < 0);
assert(doj::alphanum_comp("Alpha 2 B",str) > 0);
assert(doj::alphanum_comp(str,strdup("Alpha 2")) == 0);
assert(doj::alphanum_comp(str,strdup("Alpha 2A")) < 0);
assert(doj::alphanum_comp(strdup("Alpha 2 B"),str) > 0);
#if 1
// show usage of the comparison functor with a set
std::set<std::string, doj::alphanum_less<std::string> > s;
s.insert("Xiph Xlater 58");
s.insert("Xiph Xlater 5000");
s.insert("Xiph Xlater 500");
s.insert("Xiph Xlater 50");
s.insert("Xiph Xlater 5");
s.insert("Xiph Xlater 40");
s.insert("Xiph Xlater 300");
s.insert("Xiph Xlater 2000");
s.insert("Xiph Xlater 10000");
s.insert("QRS-62F Intrinsia Machine");
s.insert("QRS-62 Intrinsia Machine");
s.insert("QRS-60F Intrinsia Machine");
s.insert("QRS-60 Intrinsia Machine");
s.insert("Callisto Morphamax 7000 SE2");
s.insert("Callisto Morphamax 7000 SE");
s.insert("Callisto Morphamax 7000");
s.insert("Callisto Morphamax 700");
s.insert("Callisto Morphamax 600");
s.insert("Callisto Morphamax 5000");
s.insert("Callisto Morphamax 500");
s.insert("Callisto Morphamax");
s.insert("Alpha 2A-900");
s.insert("Alpha 2A-8000");
s.insert("Alpha 2A");
s.insert("Alpha 200");
s.insert("Alpha 2");
s.insert("Alpha 100");
s.insert("Allegia 60 Clasteron");
s.insert("Allegia 52 Clasteron");
s.insert("Allegia 51B Clasteron");
s.insert("Allegia 51 Clasteron");
s.insert("Allegia 500 Clasteron");
s.insert("Allegia 50 Clasteron");
s.insert("40X Radonius");
s.insert("30X Radonius");
s.insert("20X Radonius Prime");
s.insert("20X Radonius");
s.insert("200X Radonius");
s.insert("10X Radonius");
s.insert("1000X Radonius Maximus");
// print sorted set to cout
std::copy(s.begin(), s.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
// show usage of comparision functor with a map
typedef std::map<std::string, int, doj::alphanum_less<std::string> > m_t;
m_t m;
m["z1.doc"]=1;
m["z10.doc"]=2;
m["z100.doc"]=3;
m["z101.doc"]=4;
m["z102.doc"]=5;
m["z11.doc"]=6;
m["z12.doc"]=7;
m["z13.doc"]=8;
m["z14.doc"]=9;
m["z15.doc"]=10;
m["z16.doc"]=11;
m["z17.doc"]=12;
m["z18.doc"]=13;
m["z19.doc"]=14;
m["z2.doc"]=15;
m["z20.doc"]=16;
m["z3.doc"]=17;
m["z4.doc"]=18;
m["z5.doc"]=19;
m["z6.doc"]=20;
m["z7.doc"]=21;
m["z8.doc"]=22;
m["z9.doc"]=23;
// print sorted map to cout
for(m_t::iterator i=m.begin(); i!=m.end(); ++i)
std::cout << i->first << '\t' << i->second << std::endl;
// show usage of comparison functor with an STL algorithm on a vector
std::vector<std::string> v;
// vector contents are reversed sorted contents of the old set
std::copy(s.rbegin(), s.rend(), std::back_inserter(v));
// now sort the vector with the algorithm
std::sort(v.begin(), v.end(), doj::alphanum_less<std::string>());
// and print the vector to cout
std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
#endif
return 0;
}
#endif
#endif

8
dep/alphanum/build.py Normal file
View File

@@ -0,0 +1,8 @@
from build.c import clibrary
clibrary(
name="alphanum",
srcs=[],
hdrs={"dep/alphanum/alphanum.h": "./alphanum.h"},
)

View File

@@ -377,6 +377,22 @@ FluxEngine also supports a number of file system image formats. When using the
correctness. Individual records are separated by three `\\0` bytes and tracks correctness. Individual records are separated by three `\\0` bytes and tracks
are separated by four `\\0` bytes; tracks are emitted in CHS order. are separated by four `\\0` bytes; tracks are emitted in CHS order.
### Manipulating flux files
`fluxengine fluxfile` contains a set of tools for examining or manipulating
individual flux files. (Note that this only works on flux files themselves, not
sources.)
- `fluxfile ls <fluxfile>`
Shows all the components inside a flux file.
- `fluxfile rm <fluxfile>:<path>...`
Removes flux from a flux file. You may specify the path to an individual read
(e.g. `track.h0_t5.flux9`) or the track itself (`track.h0_t5`); the latter
will remove all reads from the track.
### High density disks ### High density disks
High density disks use a different magnetic medium to low and double density High density disks use a different magnetic medium to low and double density

View File

@@ -2,9 +2,14 @@ syntax = "proto2";
import "google/protobuf/descriptor.proto"; import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions { extend google.protobuf.FieldOptions
optional string help = 50000; {
optional bool recurse = 50001 [default = true]; optional string help = 50000;
}
extend google.protobuf.MessageOptions
{
optional bool recurse = 50001 [default = true];
} }
enum IndexMode { enum IndexMode {
@@ -29,21 +34,18 @@ enum FluxSourceSinkType {
FLUXTYPE_DMK = 12; FLUXTYPE_DMK = 12;
} }
enum ImageReaderWriterType { enum ImageReaderWriterType
IMAGETYPE_NOT_SET = 0; {
IMAGETYPE_D64 = 1; IMAGETYPE_NOT_SET = 0; IMAGETYPE_D64 = 1; IMAGETYPE_D88 = 2;
IMAGETYPE_D88 = 2; IMAGETYPE_DIM = 3;
IMAGETYPE_DIM = 3; IMAGETYPE_DISKCOPY = 4;
IMAGETYPE_DISKCOPY = 4; IMAGETYPE_FDI = 5;
IMAGETYPE_FDI = 5; IMAGETYPE_IMD = 6;
IMAGETYPE_IMD = 6; IMAGETYPE_IMG = 7;
IMAGETYPE_IMG = 7; IMAGETYPE_JV3 = 8;
IMAGETYPE_JV3 = 8; IMAGETYPE_LDBS = 9;
IMAGETYPE_LDBS = 9; IMAGETYPE_NFD = 10;
IMAGETYPE_NFD = 10; IMAGETYPE_NSI = 11;
IMAGETYPE_NSI = 11; IMAGETYPE_RAW = 12;
IMAGETYPE_RAW = 12; IMAGETYPE_TD0 = 13;
IMAGETYPE_TD0 = 13;
} }

View File

@@ -14,20 +14,20 @@ import "lib/config/layout.proto";
enum SupportStatus enum SupportStatus
{ {
UNSUPPORTED = 0; UNSUPPORTED = 0; DINOSAUR = 1; UNICORN = 2;
DINOSAUR = 1;
UNICORN = 2;
} }
// NEXT_TAG: 27 // NEXT_TAG: 27
message ConfigProto message ConfigProto
{ {
option(recurse) = false;
optional string shortname = 1; optional string shortname = 1;
optional string comment = 2; optional string comment = 2;
optional bool is_extension = 3; optional bool is_extension = 3;
repeated string documentation = 4; repeated string documentation = 4;
optional SupportStatus read_support_status = 5 [ default = UNSUPPORTED ]; optional SupportStatus read_support_status = 5 [default = UNSUPPORTED];
optional SupportStatus write_support_status = 6 [ default = UNSUPPORTED ]; optional SupportStatus write_support_status = 6 [default = UNSUPPORTED];
optional LayoutProto layout = 7; optional LayoutProto layout = 7;
@@ -51,28 +51,27 @@ message ConfigProto
message OptionPrerequisiteProto message OptionPrerequisiteProto
{ {
optional string key = 1 [ (help) = "path to config value" ]; optional string key = 1 [(help) = "path to config value"];
repeated string value = 2 [ (help) = "list of required values" ]; repeated string value = 2 [(help) = "list of required values"];
} }
// NEXT_TAG: 8 // NEXT_TAG: 8
message OptionProto message OptionProto
{ {
optional string name = 1 [ (help) = "option name" ]; optional string name = 1 [(help) = "option name"];
optional string comment = 2 [ (help) = "help text for option" ]; optional string comment = 2 [(help) = "help text for option"];
optional string message = 3 optional string message =
[ (help) = "message to display when option is in use" ]; 3 [(help) = "message to display when option is in use"];
optional bool set_by_default = 6 optional bool set_by_default =
[ (help) = "this option is applied by default", default = false ]; 6 [(help) = "this option is applied by default", default = false];
repeated OptionPrerequisiteProto prerequisite = 7 repeated OptionPrerequisiteProto prerequisite =
[ (help) = "prerequisites for this option" ]; 7 [(help) = "prerequisites for this option"];
optional ConfigProto config = 4 optional ConfigProto config = 4 [(help) = "option data"];
[ (help) = "option data", (recurse) = false ];
} }
message OptionGroupProto message OptionGroupProto
{ {
optional string comment = 1 [ (help) = "help text for option group" ]; optional string comment = 1 [(help) = "help text for option group"];
repeated OptionProto option = 2; repeated OptionProto option = 2;
} }

View File

@@ -264,7 +264,7 @@ static void doShowConfig()
static void doDoc() static void doDoc()
{ {
const auto fields = findAllProtoFields(globalConfig().base()); const auto fields = findAllPossibleProtoFields(globalConfig().base()->GetDescriptor());
for (const auto field : fields) for (const auto field : fields)
{ {
const std::string& path = field.first; const std::string& path = field.first;

View File

@@ -1,6 +1,7 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/config/proto.h" #include "lib/config/proto.h"
#include "lib/config/common.pb.h" #include "lib/config/common.pb.h"
#include "google/protobuf/reflection.h"
#include <regex> #include <regex>
static ConfigProto config = []() static ConfigProto config = []()
@@ -16,7 +17,7 @@ ConfigProto& globalConfigProto()
return config; return config;
} }
static double toFloat(const std::string& value) static float toFloat(const std::string& value)
{ {
try try
{ {
@@ -66,6 +67,21 @@ static uint64_t toUint64(const std::string& value)
return d; return d;
} }
static int splitIndexedField(std::string& item)
{
static const std::regex INDEX_REGEX("(\\w+)\\[([0-9]+)\\]");
int index = -1;
std::smatch dmatch;
if (std::regex_match(item, dmatch, INDEX_REGEX))
{
item = dmatch[1];
index = std::stoi(dmatch[2]);
}
return index;
}
static ProtoField resolveProtoPath( static ProtoField resolveProtoPath(
google::protobuf::Message* message, const std::string& path, bool create) google::protobuf::Message* message, const std::string& path, bool create)
{ {
@@ -86,6 +102,10 @@ static ProtoField resolveProtoPath(
std::stringstream ss(leading); std::stringstream ss(leading);
while (std::getline(ss, item, '.')) while (std::getline(ss, item, '.'))
{ {
static const std::regex INDEX_REGEX("(\\w+)\\[([0-9]+)\\]");
int index = splitIndexedField(item);
const auto* field = descriptor->FindFieldByName(item); const auto* field = descriptor->FindFieldByName(item);
if (!field) if (!field)
throw ProtoPathNotFoundException( throw ProtoPathNotFoundException(
@@ -95,6 +115,14 @@ static ProtoField resolveProtoPath(
"config field '{}' in '{}' is not a message", item, path)); "config field '{}' in '{}' is not a message", item, path));
const auto* reflection = message->GetReflection(); const auto* reflection = message->GetReflection();
if ((field->label() !=
google::protobuf::FieldDescriptor::LABEL_REPEATED) &&
(index != -1))
throw ProtoPathNotFoundException(fmt::format(
"config field '{}[{}]' is indexed, but not repeated",
item,
index));
switch (field->label()) switch (field->label())
{ {
case google::protobuf::FieldDescriptor::LABEL_OPTIONAL: case google::protobuf::FieldDescriptor::LABEL_OPTIONAL:
@@ -106,16 +134,15 @@ static ProtoField resolveProtoPath(
break; break;
case google::protobuf::FieldDescriptor::LABEL_REPEATED: case google::protobuf::FieldDescriptor::LABEL_REPEATED:
if (reflection->FieldSize(*message, field) == 0) if (index == -1)
{ throw ProtoPathNotFoundException(fmt::format(
if (create) "config field '{}' is repeated and must be indexed",
message = reflection->AddMessage(message, field); item));
else while (reflection->FieldSize(*message, field) <= index)
fail(); reflection->AddMessage(message, field);
}
else message =
message = reflection->MutableRepeatedMessage(message, field, index);
reflection->MutableRepeatedMessage(message, field, 0);
break; break;
default: default:
@@ -125,11 +152,12 @@ static ProtoField resolveProtoPath(
descriptor = message->GetDescriptor(); descriptor = message->GetDescriptor();
} }
int index = splitIndexedField(trailing);
const auto* field = descriptor->FindFieldByName(trailing); const auto* field = descriptor->FindFieldByName(trailing);
if (!field) if (!field)
fail(); fail();
return std::make_pair(message, field); return std::make_tuple(message, field, index);
} }
ProtoField makeProtoPath( ProtoField makeProtoPath(
@@ -144,91 +172,206 @@ ProtoField findProtoPath(
return resolveProtoPath(message, path, /* create= */ false); return resolveProtoPath(message, path, /* create= */ false);
} }
static bool parseBoolean(const std::string& value)
{
static const std::map<std::string, bool> boolvalues = {
{"false", false},
{"f", false},
{"no", false},
{"n", false},
{"0", false},
{"true", true },
{"t", true },
{"yes", true },
{"y", true },
{"1", true },
};
const auto& it = boolvalues.find(value);
if (it == boolvalues.end())
error("invalid boolean value");
return it->second;
}
static int32_t parseEnum(
const google::protobuf::FieldDescriptor* field, const std::string& value)
{
const auto* enumfield = field->enum_type();
const auto* enumvalue = enumfield->FindValueByName(value);
if (!enumvalue)
error("unrecognised enum value '{}'", value);
return enumvalue->number();
}
template <typename T>
static void updateRepeatedField(
google::protobuf::MutableRepeatedFieldRef<T> mrfr, int index, T value)
{
mrfr.Set(index, value);
}
void setProtoFieldFromString(ProtoField& protoField, const std::string& value) void setProtoFieldFromString(ProtoField& protoField, const std::string& value)
{ {
google::protobuf::Message* message = protoField.first; auto& [message, field, index] = protoField;
const google::protobuf::FieldDescriptor* field = protoField.second;
const auto* reflection = message->GetReflection(); const auto* reflection = message->GetReflection();
switch (field->type()) if (field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
{ {
case google::protobuf::FieldDescriptor::TYPE_FLOAT: if (index == -1)
reflection->SetFloat(message, field, toFloat(value)); error("field '{}' is repeated but no index is provided");
break;
case google::protobuf::FieldDescriptor::TYPE_DOUBLE: switch (field->type())
reflection->SetDouble(message, field, toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT32:
reflection->SetInt32(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT64:
reflection->SetInt64(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT32:
reflection->SetUInt32(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
reflection->SetUInt64(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
reflection->SetString(message, field, value);
break;
case google::protobuf::FieldDescriptor::TYPE_BOOL:
{ {
static const std::map<std::string, bool> boolvalues = { case google::protobuf::FieldDescriptor::TYPE_FLOAT:
{"false", false}, updateRepeatedField(
{"f", false}, reflection->GetMutableRepeatedFieldRef<float>(
{"no", false}, message, field),
{"n", false}, index,
{"0", false}, toFloat(value));
{"true", true },
{"t", true },
{"yes", true },
{"y", true },
{"1", true },
};
const auto& it = boolvalues.find(value);
if (it == boolvalues.end())
error("invalid boolean value");
reflection->SetBool(message, field, it->second);
break;
}
case google::protobuf::FieldDescriptor::TYPE_ENUM:
{
const auto* enumfield = field->enum_type();
const auto* enumvalue = enumfield->FindValueByName(value);
if (!enumvalue)
error("unrecognised enum value '{}'", value);
reflection->SetEnum(message, field, enumvalue);
break;
}
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->containing_oneof() && value.empty())
{
reflection->MutableMessage(message, field);
break; break;
}
/* fall through */ case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
default: updateRepeatedField(
error("can't set this config value type"); reflection->GetMutableRepeatedFieldRef<double>(
message, field),
index,
toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT32:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int32_t>(
message, field),
index,
(int32_t)toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT64:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int64_t>(
message, field),
index,
toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT32:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint32_t>(
message, field),
index,
(uint32_t)toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint64_t>(
message, field),
index,
toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<std::string>(
message, field),
index,
value);
break;
case google::protobuf::FieldDescriptor::TYPE_BOOL:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<bool>(
message, field),
index,
parseBoolean(value));
break;
case google::protobuf::FieldDescriptor::TYPE_ENUM:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int32_t>(
message, field),
index,
parseEnum(field, value));
break;
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->message_type() == RangeProto::descriptor())
{
setRange((RangeProto*)reflection->MutableRepeatedMessage(
message, field, index),
value);
break;
}
/* fall through */
default:
error("can't set this config value type");
}
}
else
{
if (index != -1)
error("field '{}' is not repeated but an index is provided");
switch (field->type())
{
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
reflection->SetFloat(message, field, toFloat(value));
break;
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
reflection->SetDouble(message, field, toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT32:
reflection->SetInt32(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT64:
reflection->SetInt64(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT32:
reflection->SetUInt32(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
reflection->SetUInt64(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
reflection->SetString(message, field, value);
break;
case google::protobuf::FieldDescriptor::TYPE_BOOL:
reflection->SetBool(message, field, parseBoolean(value));
break;
case google::protobuf::FieldDescriptor::TYPE_ENUM:
reflection->SetEnumValue(message, field, parseEnum(field, value));
break;
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->message_type() == RangeProto::descriptor())
{
setRange(
(RangeProto*)reflection->MutableMessage(message, field),
value);
break;
}
if (field->containing_oneof() && value.empty())
{
reflection->MutableMessage(message, field);
break;
}
/* fall through */
default:
error("can't set this config value type");
}
} }
} }
std::string getProtoFieldValue(ProtoField& protoField) std::string getProtoFieldValue(ProtoField& protoField)
{ {
google::protobuf::Message* message = protoField.first; auto& [message, field, index] = protoField;
const google::protobuf::FieldDescriptor* field = protoField.second;
const auto* reflection = message->GetReflection(); const auto* reflection = message->GetReflection();
switch (field->type()) switch (field->type())
@@ -294,11 +437,17 @@ std::set<unsigned> iterate(unsigned start, unsigned count)
return set; return set;
} }
static bool shouldRecurse(const google::protobuf::FieldDescriptor* f)
{
if (f->type() != google::protobuf::FieldDescriptor::TYPE_MESSAGE)
return false;
return f->message_type()->options().GetExtension(::recurse);
}
std::map<std::string, const google::protobuf::FieldDescriptor*> std::map<std::string, const google::protobuf::FieldDescriptor*>
findAllProtoFields(google::protobuf::Message* message) findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor)
{ {
std::map<std::string, const google::protobuf::FieldDescriptor*> fields; std::map<std::string, const google::protobuf::FieldDescriptor*> fields;
const auto* descriptor = message->GetDescriptor();
std::function<void(const google::protobuf::Descriptor*, const std::string&)> std::function<void(const google::protobuf::Descriptor*, const std::string&)>
recurse = [&](auto* d, const auto& s) recurse = [&](auto* d, const auto& s)
@@ -308,8 +457,10 @@ findAllProtoFields(google::protobuf::Message* message)
const google::protobuf::FieldDescriptor* f = d->field(i); const google::protobuf::FieldDescriptor* f = d->field(i);
std::string n = s + f->name(); std::string n = s + f->name();
if (f->options().GetExtension(::recurse) && if (f->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
(f->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE)) n += "[]";
if (shouldRecurse(f))
recurse(f->message_type(), n + "."); recurse(f->message_type(), n + ".");
fields[n] = f; fields[n] = f;
@@ -320,10 +471,47 @@ findAllProtoFields(google::protobuf::Message* message)
return fields; return fields;
} }
ConfigProto parseConfigBytes(const std::string_view& data) std::map<std::string, const google::protobuf::FieldDescriptor*>
findAllProtoFields(const google::protobuf::Message& message)
{ {
ConfigProto proto; std::map<std::string, const google::protobuf::FieldDescriptor*> allFields;
if (!proto.ParseFromArray(data.begin(), data.size()))
error("invalid internal config data"); std::function<void(const google::protobuf::Message&, const std::string&)>
return proto; recurse = [&](auto& message, const auto& name)
{
const auto* reflection = message.GetReflection();
std::vector<const google::protobuf::FieldDescriptor*> fields;
reflection->ListFields(message, &fields);
for (const auto* f : fields)
{
auto basename = name;
if (!basename.empty())
basename += '.';
basename += f->name();
if (f->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
{
for (int i = 0; i < reflection->FieldSize(message, f); i++)
{
const auto n = fmt::format("{}[{}]", basename, i);
if (shouldRecurse(f))
recurse(
reflection->GetRepeatedMessage(message, f, i), n);
else
allFields[n] = f;
}
}
else
{
if (shouldRecurse(f))
recurse(reflection->GetMessage(message, f), basename);
else
allFields[basename] = f;
}
}
};
recurse(message, "");
return allFields;
} }

View File

@@ -14,8 +14,8 @@ public:
} }
}; };
typedef std::pair<google::protobuf::Message*, typedef std::tuple<google::protobuf::Message*,
const google::protobuf::FieldDescriptor*> const google::protobuf::FieldDescriptor*, int>
ProtoField; ProtoField;
extern ProtoField makeProtoPath( extern ProtoField makeProtoPath(
@@ -34,9 +34,19 @@ extern std::string getProtoByString(
extern std::set<unsigned> iterate(unsigned start, unsigned count); extern std::set<unsigned> iterate(unsigned start, unsigned count);
extern std::map<std::string, const google::protobuf::FieldDescriptor*> extern std::map<std::string, const google::protobuf::FieldDescriptor*>
findAllProtoFields(google::protobuf::Message* message); findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor);
extern ConfigProto parseConfigBytes(const std::string_view& bytes); extern std::map<std::string, const google::protobuf::FieldDescriptor*>
findAllProtoFields(const google::protobuf::Message& message);
template <class T>
static inline const T parseProtoBytes(const std::string_view& bytes)
{
T proto;
if (!proto.ParseFromArray(bytes.begin(), bytes.size()))
error("invalid internal proto data");
return proto;
}
extern const std::map<std::string, const ConfigProto*> formats; extern const std::map<std::string, const ConfigProto*> formats;

View File

@@ -24,6 +24,9 @@
#define mkdir(A, B) _mkdir(A) #define mkdir(A, B) _mkdir(A)
#endif #endif
#define STRINGIFY(a) XSTRINGIFY(a)
#define XSTRINGIFY(a) #a
template <class T> template <class T>
static inline std::vector<T> vector_of(T item) static inline std::vector<T> vector_of(T item)
{ {

View File

@@ -1,5 +1,12 @@
syntax = "proto2"; syntax = "proto2";
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions
{
optional bool isflux = 60000 [default = false];
}
enum FluxMagic { enum FluxMagic {
MAGIC = 0x466c7578; MAGIC = 0x466c7578;
} }
@@ -12,7 +19,7 @@ enum FluxFileVersion {
message TrackFluxProto { message TrackFluxProto {
optional int32 track = 1; optional int32 track = 1;
optional int32 head = 2; optional int32 head = 2;
repeated bytes flux = 3; repeated bytes flux = 3 [(isflux) = true];
} }
enum DriveType { enum DriveType {

View File

@@ -5,13 +5,14 @@ encoders = {}
@Rule @Rule
def protoencode_single(self, name, srcs: Targets, proto, symbol): def protoencode_single(self, name, srcs: Targets, proto, include, symbol):
if proto not in encoders: if proto not in encoders:
r = cxxprogram( r = cxxprogram(
name="protoencode_" + proto, name="protoencode_" + proto,
srcs=["scripts/protoencode.cc"], srcs=["scripts/protoencode.cc"],
cflags=["-DPROTO=" + proto], cflags=["-DPROTO=" + proto, "-DINCLUDE="+include],
deps=[ deps=[
"lib/core",
"lib/config+proto_lib", "lib/config+proto_lib",
"lib/fluxsource+proto_lib", "lib/fluxsource+proto_lib",
"lib/fluxsink+proto_lib", "lib/fluxsink+proto_lib",
@@ -41,12 +42,13 @@ def protoencode_single(self, name, srcs: Targets, proto, symbol):
@Rule @Rule
def protoencode(self, name, proto, srcs: TargetsMap, symbol): def protoencode(self, name, proto, include,srcs: TargetsMap, symbol):
encoded = [ encoded = [
protoencode_single( protoencode_single(
name=f"{k}_cc", name=f"{k}_cc",
srcs=[v], srcs=[v],
proto=proto, proto=proto,
include=include,
symbol=f"{symbol}_{k}_pb", symbol=f"{symbol}_{k}_pb",
) )
for k, v in srcs.items() for k, v in srcs.items()

View File

@@ -3,12 +3,13 @@
#include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/io/zero_copy_stream_impl.h>
#include <fstream> #include <fstream>
#include "fmt/format.h" #include "fmt/format.h"
#include "lib/core/globals.h"
#include "tests/testproto.pb.h" #include "tests/testproto.pb.h"
#include "lib/config/config.pb.h" #include "lib/config/config.pb.h"
#include <sstream> #include <sstream>
#include <locale> #include <locale>
#define STRINGIFY(s) #s const std::string protoname = STRINGIFY(PROTO);
static uint32_t readu8(std::string::iterator& it, std::string::iterator end) static uint32_t readu8(std::string::iterator& it, std::string::iterator end)
{ {
@@ -125,6 +126,7 @@ int main(int argc, const char* argv[])
output << "#include \"lib/core/globals.h\"\n" output << "#include \"lib/core/globals.h\"\n"
<< "#include \"lib/config/proto.h\"\n" << "#include \"lib/config/proto.h\"\n"
<< "#include \"" STRINGIFY(INCLUDE) "\"\n"
<< "#include <string_view>\n" << "#include <string_view>\n"
<< "static const uint8_t " << name << "_rawData[] = {"; << "static const uint8_t " << name << "_rawData[] = {";
@@ -143,11 +145,11 @@ int main(int argc, const char* argv[])
output << "\n};\n"; output << "\n};\n";
output << "extern const std::string_view " << name << "_data;\n"; output << "extern const std::string_view " << name << "_data;\n";
output << "const std::string_view " << name output << "const std::string_view " << name
<< "_data = std::string_view((const char*)" << name << "_rawData, " << data.size() << "_data = std::string_view((const char*)" << name << "_rawData, "
<< ");\n"; << data.size() << ");\n";
output << "extern const ConfigProto " << name << ";\n"; output << "extern const " << protoname << " " << name << ";\n";
output << "const ConfigProto " << name << " = parseConfigBytes(" output << "const " << protoname << " " << name << " = parseProtoBytes<"
<< argv[3] << "_data);\n"; << protoname << ">(" << argv[3] << "_data);\n";
return 0; return 0;
} }

View File

@@ -5,10 +5,14 @@ cxxprogram(
srcs=[ srcs=[
"./fluxengine.cc", "./fluxengine.cc",
"./fluxengine.h", "./fluxengine.h",
"./fluxfile.cc",
"./fluxfile.h",
"./fe-analysedriveresponse.cc", "./fe-analysedriveresponse.cc",
"./fe-analyselayout.cc", "./fe-analyselayout.cc",
"./fe-convert.cc", "./fe-convert.cc",
"./fe-format.cc", "./fe-format.cc",
"./fe-fluxfilels.cc",
"./fe-fluxfilerm.cc",
"./fe-getdiskinfo.cc", "./fe-getdiskinfo.cc",
"./fe-getfile.cc", "./fe-getfile.cc",
"./fe-getfileinfo.cc", "./fe-getfileinfo.cc",
@@ -39,6 +43,7 @@ cxxprogram(
"+z_lib", "+z_lib",
"dep/adflib", "dep/adflib",
"dep/agg", "dep/agg",
"dep/alphanum",
"dep/fatfs", "dep/fatfs",
"dep/hfsutils", "dep/hfsutils",
"dep/libusbp", "dep/libusbp",

39
src/fe-fluxfilels.cc Normal file
View File

@@ -0,0 +1,39 @@
#include "lib/core/globals.h"
#include "lib/config/flags.h"
#include "lib/data/fluxmap.h"
#include "lib/data/sector.h"
#include "lib/config/proto.h"
#include "lib/data/flux.h"
#include "lib/external/fl2.h"
#include "lib/external/fl2.pb.h"
#include "dep/alphanum/alphanum.h"
#include "src/fluxengine.h"
#include <fstream>
static FlagGroup flags;
static std::string filename;
int mainFluxfileLs(int argc, const char* argv[])
{
const auto filenames = flags.parseFlagsWithFilenames(argc, argv);
if (filenames.size() != 1)
error("you must specify exactly one filename");
for (const auto& filename : filenames)
{
fmt::print("Contents of {}:\n", filename);
FluxFileProto f = loadFl2File(filename);
auto fields = findAllProtoFields(f);
std::set<std::string, doj::alphanum_less<std::string>> fieldsSorted;
for (const auto& e : fields)
fieldsSorted.insert(e.first);
for (const auto& e : fieldsSorted)
{
fmt::print("{}: {}\n", e, getProtoByString(&f, e));
}
}
return 0;
}

47
src/fe-fluxfilerm.cc Normal file
View File

@@ -0,0 +1,47 @@
#include "lib/core/globals.h"
#include "lib/config/flags.h"
#include "lib/data/fluxmap.h"
#include "lib/data/sector.h"
#include "lib/config/proto.h"
#include "lib/data/flux.h"
#include "lib/external/fl2.h"
#include "lib/external/fl2.pb.h"
#include "src/fluxengine.h"
#include <fstream>
static FlagGroup flags;
static std::string filename;
int mainFluxfileRm(int argc, const char* argv[])
{
const auto filenames = flags.parseFlagsWithFilenames(argc, argv);
if (filenames.size() != 1)
error("you must specify exactly one filename");
const auto& filename = *filenames.begin();
fmt::print("Contents of {}:\n", filename);
FluxFileProto f = loadFl2File(filename);
fmt::print("version: {}\n", getProtoByString(&f, "version"));
fmt::print("rotational_period_ms: {}\n",
getProtoByString(&f, "rotational_period_ms"));
fmt::print("drive_type: {}\n", getProtoByString(&f, "drive_type"));
fmt::print("format_type: {}\n", getProtoByString(&f, "format_type"));
for (const auto& track : f.track())
{
for (int i = 0; i < track.flux().size(); i++)
{
const auto& flux = track.flux().at(i);
Fluxmap fluxmap(flux);
fmt::print("track.t{}_h{}.flux{}: {:.3f} ms, {} bytes\n",
track.track(),
track.head(),
i,
fluxmap.duration() / 1000000,
fluxmap.bytes());
}
}
return 0;
}

View File

@@ -7,6 +7,8 @@ typedef int command_cb(int agrc, const char* argv[]);
extern command_cb mainAnalyseDriveResponse; extern command_cb mainAnalyseDriveResponse;
extern command_cb mainAnalyseLayout; extern command_cb mainAnalyseLayout;
extern command_cb mainConvert; extern command_cb mainConvert;
extern command_cb mainFluxfileLs;
extern command_cb mainFluxfileRm;
extern command_cb mainFormat; extern command_cb mainFormat;
extern command_cb mainGetDiskInfo; extern command_cb mainGetDiskInfo;
extern command_cb mainGetFile; extern command_cb mainGetFile;
@@ -36,6 +38,7 @@ struct Command
}; };
static command_cb mainAnalyse; static command_cb mainAnalyse;
static command_cb mainFluxfile;
static command_cb mainTest; static command_cb mainTest;
// clang-format off // clang-format off
@@ -45,6 +48,7 @@ static std::vector<Command> commands =
{ "analyse", mainAnalyse, "Disk and drive analysis tools." }, { "analyse", mainAnalyse, "Disk and drive analysis tools." },
{ "read", mainRead, "Reads a disk, producing a sector image.", }, { "read", mainRead, "Reads a disk, producing a sector image.", },
{ "write", mainWrite, "Writes a sector image to a disk.", }, { "write", mainWrite, "Writes a sector image to a disk.", },
{ "fluxfile", mainFluxfile, "Flux file manipulation operations.", },
{ "format", mainFormat, "Format a disk and make a file system on it.", }, { "format", mainFormat, "Format a disk and make a file system on it.", },
{ "rawread", mainRawRead, "Reads raw flux from a disk. Warning: you can't use this to copy disks.", }, { "rawread", mainRawRead, "Reads raw flux from a disk. Warning: you can't use this to copy disks.", },
{ "rawwrite", mainRawWrite, "Writes a flux file to a disk. Warning: you can't use this to copy disks.", }, { "rawwrite", mainRawWrite, "Writes a flux file to a disk. Warning: you can't use this to copy disks.", },
@@ -71,9 +75,15 @@ static std::vector<Command> analysables =
static std::vector<Command> testables = static std::vector<Command> testables =
{ {
{ "bandwidth", mainTestBandwidth, "Measures your USB bandwidth.", }, { "bandwidth", mainTestBandwidth, "Measures your USB bandwidth.", },
{ "devices", mainTestDevices, "Displays all detected devices.", }, { "devices", mainTestDevices, "Displays all detected devices.", },
{ "voltages", mainTestVoltages, "Measures the FDD bus voltages.", }, { "voltages", mainTestVoltages, "Measures the FDD bus voltages.", },
};
static std::vector<Command> fluxfileables =
{
{ "ls", mainFluxfileLs, "Lists the contents of a flux file.", },
{ "rm", mainFluxfileRm, "Removes flux from a flux file.", },
}; };
// clang-format on // clang-format on
@@ -122,6 +132,11 @@ static int mainTest(int argc, const char* argv[])
return mainExtended(testables, "test", argc, argv); return mainExtended(testables, "test", argc, argv);
} }
static int mainFluxfile(int argc, const char* argv[])
{
return mainExtended(fluxfileables, "fluxfile", argc, argv);
}
static void globalHelp() static void globalHelp()
{ {
std::cout << "fluxengine: syntax: fluxengine <command> [<flags>...]\n" std::cout << "fluxengine: syntax: fluxengine <command> [<flags>...]\n"

0
src/fluxfile.cc Normal file
View File

4
src/fluxfile.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
extern void parseFluxfilePath(
const std::string& combined, std::string& filename, std::string& path);

View File

@@ -58,6 +58,7 @@ protoencode(
name="formats_cc", name="formats_cc",
srcs={name: f"./{name}.textpb" for name in formats}, srcs={name: f"./{name}.textpb" for name in formats},
proto="ConfigProto", proto="ConfigProto",
include="lib/config/config.pb.h",
symbol="formats", symbol="formats",
) )

View File

@@ -26,6 +26,7 @@ protoencode(
name="drivetypes_cc", name="drivetypes_cc",
srcs={name: f"./{name}.textpb" for name in drivetypes}, srcs={name: f"./{name}.textpb" for name in drivetypes},
proto="ConfigProto", proto="ConfigProto",
include="lib/config/config.pb.h",
symbol="drivetypes", symbol="drivetypes",
) )

View File

@@ -40,44 +40,38 @@ tests = [
"vfs", "vfs",
] ]
protoencode_single(
name="testproto_cc",
srcs=["./testproto.textpb"],
proto="TestProto",
include="tests/testproto.pb.h",
symbol="testproto_pb",
)
export( export(
name="tests", name="tests",
deps=[ deps=[
test( test(
name="proto_test", name=f"{n}_test",
command=cxxprogram( command=cxxprogram(
name="proto_test_exe", name=f"{n}_test_exe",
srcs=[ srcs=[
"./proto.cc", f"./{n}.cc",
protoencode_single( ".+testproto_cc",
name="testproto_cc",
srcs=["./testproto.textpb"],
proto="TestProto",
symbol="testproto_pb",
),
], ],
deps=[ deps=[
"lib/external+fl2_proto_lib",
"+fmt_lib", "+fmt_lib",
"+protobuf_lib", "+protobuf_lib",
"+protocol", "+protocol",
"+z_lib",
".+test_proto_lib", ".+test_proto_lib",
"dep/adflib",
"dep/agg",
"dep/fatfs",
"dep/hfsutils",
"dep/libusbp",
"dep/snowhouse", "dep/snowhouse",
"dep/stb",
"lib/config", "lib/config",
"lib/core", "lib/core",
"lib/data",
"lib/fluxsource+proto_lib", "lib/fluxsource+proto_lib",
"src/formats",
], ],
), ),
), )
for n in ["proto"]
] ]
+ [ + [
test( test(

View File

@@ -28,8 +28,9 @@ static void test_setting(void)
setProtoByString(&config, "d", "5.5"); setProtoByString(&config, "d", "5.5");
setProtoByString(&config, "f", "6.7"); setProtoByString(&config, "f", "6.7");
setProtoByString(&config, "m.s", "string"); setProtoByString(&config, "m.s", "string");
setProtoByString(&config, "r.s", "val1"); setProtoByString(&config, "r[0].s", "val1");
setProtoByString(&config, "r.s", "val2"); setProtoByString(&config, "r[0].s", "val2");
setProtoByString(&config, "r[1].s", "val3");
setProtoByString(&config, "firstoption.s", "1"); setProtoByString(&config, "firstoption.s", "1");
setProtoByString(&config, "secondoption.s", "2"); setProtoByString(&config, "secondoption.s", "2");
@@ -48,6 +49,9 @@ static void test_setting(void)
r { r {
s: "val2" s: "val2"
} }
r {
s: "val3"
}
secondoption { secondoption {
s: "2" s: "2"
} }
@@ -70,8 +74,14 @@ static void test_getting(void)
r { r {
s: "val2" s: "val2"
} }
r {
s: "val3"
}
secondoption { secondoption {
s: "2" s: "2"
r: 0
r: 1
r: 2
} }
)M"; )M";
@@ -86,9 +96,11 @@ static void test_getting(void)
AssertThat(getProtoByString(&tp, "d"), Equals("5.5")); AssertThat(getProtoByString(&tp, "d"), Equals("5.5"));
AssertThat(getProtoByString(&tp, "f"), Equals("6.7")); AssertThat(getProtoByString(&tp, "f"), Equals("6.7"));
AssertThat(getProtoByString(&tp, "m.s"), Equals("string")); AssertThat(getProtoByString(&tp, "m.s"), Equals("string"));
AssertThat(getProtoByString(&tp, "r.s"), Equals("val2")); AssertThat(getProtoByString(&tp, "r[0].s"), Equals("val2"));
AssertThat(getProtoByString(&tp, "r[1].s"), Equals("val3"));
AssertThrows( AssertThrows(
ProtoPathNotFoundException, getProtoByString(&tp, "firstoption.s")); ProtoPathNotFoundException, getProtoByString(&tp, "firstoption.s"));
AssertThat(getProtoByString(&tp, "secondoption.r[2]"), Equals("2"));
AssertThat(getProtoByString(&tp, "secondoption.s"), Equals("2")); AssertThat(getProtoByString(&tp, "secondoption.s"), Equals("2"));
} }
@@ -131,8 +143,31 @@ static void test_load(void)
static void test_fields(void) static void test_fields(void)
{ {
TestProto proto; TestProto proto;
auto fields = findAllProtoFields(&proto); auto fields = findAllPossibleProtoFields(proto.GetDescriptor());
AssertThat(fields.size(), Equals(14)); std::vector<std::string> fieldNames;
for (const auto& e : fields)
fieldNames.push_back(e.first);
AssertThat(fieldNames,
Equals(std::vector<std::string>{"d",
"f",
"firstoption",
"firstoption.r[]",
"firstoption.s",
"i32",
"i64",
"m",
"m.r[]",
"m.s",
"r[]",
"r[].r[]",
"r[].s",
"range",
"secondoption",
"secondoption.r[]",
"secondoption.s",
"u32",
"u64"}));
} }
static void test_options(void) static void test_options(void)
@@ -145,6 +180,57 @@ static void test_options(void)
AssertThat(s, Equals("i64")); AssertThat(s, Equals("i64"));
} }
static void test_findallfields(void)
{
std::string s = R"M(
i64: -1
i32: -2
u64: 3
u32: 4
d: 5.5
f: 6.7
m {
s: "string"
}
r {
s: "val2"
}
r {
s: "val3"
}
secondoption {
s: "2"
}
range {
start: 1
step: 2
end: 3
}
)M";
TestProto proto;
if (!google::protobuf::TextFormat::MergeFromString(cleanup(s), &proto))
error("couldn't load test proto");
auto fields = findAllProtoFields(proto);
std::vector<std::string> fieldNames;
for (const auto& e : fields)
fieldNames.push_back(e.first);
AssertThat(fieldNames,
Equals(std::vector<std::string>{"d",
"f",
"i32",
"i64",
"m.s",
"r[0].s",
"r[1].s",
"range",
"secondoption.s",
"u32",
"u64"}));
}
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
try try
@@ -155,6 +241,7 @@ int main(int argc, const char* argv[])
test_load(); test_load();
test_fields(); test_fields();
test_options(); test_options();
test_findallfields();
} }
catch (const ErrorException& e) catch (const ErrorException& e)
{ {

View File

@@ -2,23 +2,25 @@ syntax = "proto2";
import "lib/config/common.proto"; import "lib/config/common.proto";
message TestProto { message TestProto
message SubMessageProto { {
optional string s = 1; message SubMessageProto
} {
optional string s = 1;
repeated int32 r = 2;
}
optional int64 i64 = 1 [(help)="i64"]; optional int64 i64 = 1 [(help) = "i64"];
optional int32 i32 = 2; optional int32 i32 = 2;
optional uint64 u64 = 3; optional uint64 u64 = 3;
optional uint32 u32 = 4; optional uint32 u32 = 4;
optional double d = 5; optional double d = 5;
optional double f = 11; optional double f = 11;
optional SubMessageProto m = 6; optional SubMessageProto m = 6;
repeated SubMessageProto r = 7; repeated SubMessageProto r = 7;
oneof alt { oneof alt {
SubMessageProto firstoption = 8; SubMessageProto firstoption = 8;
SubMessageProto secondoption = 9; SubMessageProto secondoption = 9;
} }
} }