diff --git a/abi/main.cpp b/abi/main.cpp
index e895152cc..cdae81f1e 100644
--- a/abi/main.cpp
+++ b/abi/main.cpp
@@ -43,8 +43,8 @@ void help()
<< " -h,--help Print this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl
<< "Input options:" << endl
- << " -p,--prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl
- << " -P,--no-prefix Require no input format to be prefixed." << endl
+ << " -f,--format-prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl
+ << " -F,--no-format-prefix Require no input format to be prefixed." << endl
<< " -t,--typing Require all arguments to be typed e.g. b32: (bytes32), u64: (uint64), b[]: (byte[]), i: (int256)." << endl
<< " -T,--no-typing Require no arguments to be typed." << endl
<< "Output options:" << endl
@@ -53,8 +53,6 @@ void help()
<< " -x,--hex Display all data as hex." << endl
<< " -b,--binary Display all data as binary." << endl
<< " -p,--prefix Prefix by a base identifier." << endl
- << " -z,--no-zeroes Remove any leading zeroes from the data." << endl
- << " -n,--no-nulls Remove any trailing nulls from the data." << endl
;
exit(0);
}
@@ -90,7 +88,9 @@ enum class Format
{
Binary,
Hex,
- Decimal
+ Decimal,
+ Open,
+ Close
};
struct InvalidUserString: public Exception {};
@@ -115,6 +115,12 @@ static const map s_bases =
{ Base::Fixed, "fixed" }
};
+struct EncodingPrefs
+{
+ Encoding e = Encoding::Auto;
+ bool prefix = true;
+};
+
struct ABIType
{
Base base = Base::Unknown;
@@ -204,39 +210,72 @@ struct ABIType
bool isBytes() const { return base == Base::Bytes && !size; }
- string render(bytes const& _data) const
+ string render(bytes const& _data, EncodingPrefs _e) const
{
- if (base == Base::Uint)
- return toString(fromBigEndian(_data));
- else if (base == Base::Int)
- return toString((s256)fromBigEndian(_data));
+ if (base == Base::Uint || base == Base::Int)
+ {
+ if (_e.e == Encoding::Hex)
+ return (_e.prefix ? "0x" : "") + toHex(toCompactBigEndian(fromBigEndian(bytesConstRef(&_data).cropped(32 - size / 8))));
+ else
+ {
+ bigint i = fromBigEndian(bytesConstRef(&_data).cropped(32 - size / 8));
+ if (base == Base::Int && i > (bigint(1) << (size - 1)))
+ i -= (bigint(1) << size);
+ return toString(i);
+ }
+ }
else if (base == Base::Address)
- return toString(Address(h256(_data)));
+ {
+ Address a = Address(h256(_data), Address::AlignRight);
+ return _e.e == Encoding::Binary ? asString(a.asBytes()) : ((_e.prefix ? "0x" : "") + toString(a));
+ }
+ else if (isBytes())
+ {
+ return _e.e == Encoding::Binary ? asString(_data) : ((_e.prefix ? "0x" : "") + toHex(_data));
+ }
+ else if (base == Base::Bytes)
+ {
+ bytesConstRef b(&_data);
+ b = b.cropped(0, size);
+ return _e.e == Encoding::Binary ? asString(b) : ((_e.prefix ? "0x" : "") + toHex(b));
+ }
+ else
+ throw InvalidFormat();
+ }
+
+ bytes unrender(bytes const& _data, Format _f) const
+ {
+ if (isBytes())
+ {
+ auto ret = _data;
+ while (ret.size() % 32 != 0)
+ ret.push_back(0);
+ return ret;
+ }
else
- return toHex(_data);
+ return aligned(_data, _f, 32);
}
void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } }
void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } }
void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } }
-};
-bytes aligned(bytes const& _b, ABIType _t, Format _f, unsigned _length)
-{
- (void)_t;
- bytes ret = _b;
- while (ret.size() < _length)
- if (_f == Format::Binary)
- ret.push_back(0);
- else
- ret.insert(ret.begin(), 0);
- while (ret.size() > _length)
- if (_f == Format::Binary)
- ret.pop_back();
- else
- ret.erase(ret.begin());
- return ret;
-}
+ bytes aligned(bytes const& _b, Format _f, unsigned _length) const
+ {
+ bytes ret = _b;
+ while (ret.size() < _length)
+ if (_f == Format::Binary)
+ ret.push_back(0);
+ else
+ ret.insert(ret.begin(), 0);
+ while (ret.size() > _length)
+ if (_f == Format::Binary)
+ ret.pop_back();
+ else
+ ret.erase(ret.begin());
+ return ret;
+ }
+};
tuple fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing)
{
@@ -269,25 +308,36 @@ tuple fromUser(std::string const& _arg, Tristate _prefix
type.noteBinaryInput();
return make_tuple(asBytes(val.substr(1)), type, Format::Binary);
}
+ if (val == "[")
+ return make_tuple(bytes(), type, Format::Open);
+ if (val == "]")
+ return make_tuple(bytes(), type, Format::Close);
}
if (_prefix != Tristate::True)
{
- if (_arg.find_first_not_of("0123456789") == string::npos)
+ if (val.find_first_not_of("0123456789") == string::npos)
{
type.noteDecimalInput();
return make_tuple(toCompactBigEndian(bigint(val)), type, Format::Decimal);
}
- if (_arg.find_first_not_of("0123456789abcdefABCDEF") == string::npos)
+ if (val.find_first_not_of("0123456789abcdefABCDEF") == string::npos)
{
type.noteHexInput(val.size());
return make_tuple(fromHex(val), type, Format::Hex);
}
+ if (val == "[")
+ return make_tuple(bytes(), type, Format::Open);
+ if (val == "]")
+ return make_tuple(bytes(), type, Format::Close);
type.noteBinaryInput();
- return make_tuple(asBytes(_arg), type, Format::Binary);
+ return make_tuple(asBytes(val), type, Format::Binary);
}
throw InvalidUserString();
}
+struct ExpectedOpen: public Exception {};
+struct ExpectedClose: public Exception {};
+
struct ABIMethod
{
string name;
@@ -355,77 +405,107 @@ struct ABIMethod
bytes encode(vector> const& _params) const
{
- // ALL WRONG!!!!
- // INARITIES SHOULD BE HEIRARCHICAL!
-
bytes ret = name.empty() ? bytes() : id().asBytes();
+ bytes suffix;
+
+ // int int[] int
+ // example: 42 [ 1 2 3 ] 69
+ // int[2][][3]
+ // example: [ [ [ 1 2 3 ] [ 4 5 6 ] ] [ ] ]
+
unsigned pi = 0;
- vector inArity;
- for (ABIType const& i: ins)
- {
- unsigned arity = 1;
- for (auto j: i.dims)
- if (j == -1)
- {
- ret += aligned(_params[pi].first, ABIType(), Format::Decimal, 32);
- arity *= fromBigEndian(_params[pi].first);
- pi++;
- }
- else
- arity *= j;
- if (i.isBytes())
- for (unsigned i = 0; i < arity; ++i)
- inArity.push_back(arity);
- }
- unsigned ii = 0;
- for (ABIType const& i: ins)
+ for (ABIType const& a: ins)
{
- for (unsigned j = 0; j < inArity[ii]; ++j)
- {
- if (i.base == Base::Bytes && !i.size)
+ auto put = [&]() {
+ if (a.isBytes())
+ ret += h256(u256(_params[pi].first.size())).asBytes();
+ suffix += a.unrender(_params[pi].first, _params[pi].second);
+ pi++;
+ };
+ function, unsigned)> putDim = [&](vector addr, unsigned q) {
+ if (addr.size() == a.dims.size())
+ put();
+ else
{
- ret += _params[pi].first;
- while (ret.size() % 32 != 0)
- ret.push_back(0);
+ if (_params[pi].second != Format::Open)
+ throw ExpectedOpen();
+ int l = a.dims[addr.size()];
+ if (l == -1)
+ {
+ // read ahead in params and discover the arity.
+ unsigned depth = 0;
+ l = 0;
+ for (unsigned pi2 = pi + 1; depth || _params[pi2].second != Format::Close;)
+ {
+ if (_params[pi2].second == Format::Open)
+ ++depth;
+ if (_params[pi2].second == Format::Close)
+ --depth;
+ if (!depth)
+ ++l;
+ if (++pi2 == _params.size())
+ throw ExpectedClose();
+ }
+ ret += h256(u256(l)).asBytes();
+ }
+ q *= l;
+ for (addr.push_back(0); addr.back() < l; ++addr.back())
+ putDim(addr, q);
+ if (_params[pi].second != Format::Close)
+ throw ExpectedClose();
}
- else
- ret += aligned(_params[pi].first, i, _params[pi].second, 32);
- ++pi;
- }
- ++ii;
+ };
+ putDim(vector(), 1);
}
- return ret;
+ return ret + suffix;
}
- string decode(bytes const& _data, int _index = -1)
+ string decode(bytes const& _data, int _index, EncodingPrefs _ep)
{
stringstream out;
if (_index == -1)
out << "[";
unsigned di = 0;
- vector souts;
vector catDims;
- for (ABIType a: outs)
+ for (ABIType const& a: outs)
{
- unsigned q = 1;
- for (auto& i: a.dims)
- {
- for (unsigned j = 0; j < q; ++j)
- if (i == -1)
+ auto put = [&]() {
+ if (a.isBytes())
+ {
+ catDims.push_back(fromBigEndian(bytesConstRef(&_data).cropped(di, 32)));
+ di += 32;
+ }
+ };
+ function, unsigned)> putDim = [&](vector addr, unsigned q) {
+ if (addr.size() == a.dims.size())
+ put();
+ else
+ {
+ out << "[";
+ int l = a.dims[addr.size()];
+ if (l == -1)
{
- catDims.push_back(fromBigEndian(bytesConstRef(&_data).cropped(di, 32)));
+ l = fromBigEndian(bytesConstRef(&_data).cropped(di, 32));
+ catDims.push_back(l);
di += 32;
}
- q *= i;
- }
- if (a.isBytes())
- souts.push_back(a);
+ q *= l;
+ for (addr.push_back(0); addr.back() < l; ++addr.back())
+ putDim(addr, q);
+ out << "]";
+ }
+ };
+ putDim(vector(), 1);
}
- for (ABIType const& a: souts)
+ unsigned d = 0;
+ for (ABIType const& a: outs)
{
auto put = [&]() {
- out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes()) << ", ";
- di += 32;
+ unsigned l = 32;
+ if (a.isBytes())
+ l = (catDims[d++] + 31 / 32) * 32;
+ out << a.render(bytesConstRef(&_data).cropped(di, l).toBytes(), _ep) << ", ";
+ di += l;
};
function)> putDim = [&](vector addr) {
if (addr.size() == a.dims.size())
@@ -433,9 +513,11 @@ struct ABIMethod
else
{
out << "[";
- auto d = addr;
addr.push_back(0);
- for (addr.back() = 0; addr.back() < a.dims[addr.size() - 1]; ++addr.back())
+ int l = a.dims[addr.size() - 1];
+ if (l == -1)
+ l = catDims[d++];
+ for (addr.back() = 0; addr.back() < l; ++addr.back())
{
if (addr.back())
out << ", ";
@@ -543,10 +625,9 @@ int main(int argc, char** argv)
Mode mode = Mode::Encode;
string abiFile;
string method;
- Tristate prefix = Tristate::Mu;
+ Tristate formatPrefix = Tristate::Mu;
Tristate typePrefix = Tristate::Mu;
- bool clearZeroes = false;
- bool clearNulls = false;
+ EncodingPrefs prefs;
bool verbose = false;
int outputIndex = -1;
vector> params;
@@ -566,25 +647,23 @@ int main(int argc, char** argv)
else if ((arg == "-i" || arg == "--index") && argc > i)
outputIndex = atoi(argv[++i]);
else if (arg == "-p" || arg == "--prefix")
- prefix = Tristate::True;
- else if (arg == "-P" || arg == "--no-prefix")
- prefix = Tristate::False;
+ prefs.prefix = true;
+ else if (arg == "-f" || arg == "--format-prefix")
+ formatPrefix = Tristate::True;
+ else if (arg == "-F" || arg == "--no-format-prefix")
+ formatPrefix = Tristate::False;
else if (arg == "-t" || arg == "--typing")
typePrefix = Tristate::True;
else if (arg == "-T" || arg == "--no-typing")
typePrefix = Tristate::False;
- else if (arg == "-z" || arg == "--no-zeroes")
- clearZeroes = true;
- else if (arg == "-n" || arg == "--no-nulls")
- clearNulls = true;
else if (arg == "-v" || arg == "--verbose")
verbose = true;
else if (arg == "-x" || arg == "--hex")
- encoding = Encoding::Hex;
+ prefs.e = Encoding::Hex;
else if (arg == "-d" || arg == "--decimal" || arg == "--dec")
- encoding = Encoding::Decimal;
+ prefs.e = Encoding::Decimal;
else if (arg == "-b" || arg == "--binary" || arg == "--bin")
- encoding = Encoding::Binary;
+ prefs.e = Encoding::Binary;
else if (arg == "-v" || arg == "--verbose")
version();
else if (arg == "-V" || arg == "--version")
@@ -593,7 +672,7 @@ int main(int argc, char** argv)
method = arg;
else
{
- auto u = fromUser(arg, prefix, typePrefix);
+ auto u = fromUser(arg, formatPrefix, typePrefix);
args.push_back(get<1>(u));
params.push_back(make_pair(get<0>(u), get<2>(u)));
}
@@ -648,14 +727,8 @@ int main(int argc, char** argv)
string encoded;
for (int i = cin.get(); i != -1; i = cin.get())
encoded.push_back((char)i);
- cout << m.decode(fromHex(encoded));
+ cout << m.decode(fromHex(encoded), outputIndex, prefs);
}
-
- // TODO: read abi to determine output format.
- (void)encoding;
- (void)clearZeroes;
- (void)clearNulls;
- (void)outputIndex;
}
return 0;
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h
index 1c7a4cd61..6468e250f 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -42,14 +42,15 @@ enum class WhenError
};
/// Convert a series of bytes to the corresponding string of hex duplets.
-/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte.
+/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169"
template
std::string toHex(_T const& _data, int _w = 2)
{
std::ostringstream ret;
+ unsigned ii = 0;
for (auto i: _data)
- ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned::type)i;
+ ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned::type)i;
return ret.str();
}
@@ -74,6 +75,13 @@ inline std::string asString(bytes const& _b)
return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size()));
}
+/// Converts byte array ref to a string containing the same (binary) data. Unless
+/// the byte array happens to contain ASCII data, this won't be printable.
+inline std::string asString(bytesConstRef _b)
+{
+ return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size()));
+}
+
/// Converts a string to a byte array containing the string's (byte) data.
inline bytes asBytes(std::string const& _b)
{