diff --git a/Gemfile.lock b/Gemfile.lock index 50cf03106..0497e8bbf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - xdrgen (0.1.1) + xdrgen (0.1.3) activesupport (~> 6) memoist (~> 0.11.0) slop (~> 3.4) diff --git a/README.md b/README.md index aaaa917d1..751d211bc 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,12 @@ The command line: `xdrgen [-o OUTPUT_DIR] [-l LANGUAGE] [-n NAMESPACE] [INPUT_FILES ...]` +### Language Specific Options + +### Rust + +`--rust-types-custom-str-impl`: Used to specify a comma-separated list of type names that should not have string conversion code generated as it will be provided by custom implementations provided by the developer using the generated code. + ## Usage as a library Add this line to your application's Gemfile: @@ -64,7 +70,10 @@ c = Xdrgen::Compilation.new( ["MyProgram.x"], output_dir:"src/generated", language: :ruby, - namespace: "MyProgram::XDR" + namespace: "MyProgram::XDR", + options: { + rust_types_custom_str_impl: [], + }, ) # then run compile diff --git a/lib/xdrgen/cli.rb b/lib/xdrgen/cli.rb index 1fd6e6dc9..f818c51f5 100644 --- a/lib/xdrgen/cli.rb +++ b/lib/xdrgen/cli.rb @@ -9,6 +9,7 @@ def self.run(args) on 'o', 'output=', 'The output directory' on 'l', 'language=', 'The output language', default: 'ruby' on 'n', 'namespace=', '"namespace" to generate code within (language-specific)' + on 'rust-types-custom-str-impl=', 'Rust types that should not have str implementations generated as they will be provided via custom implementations (rust-specific)' end fail(opts) if args.blank? @@ -18,7 +19,10 @@ def self.run(args) args, output_dir: opts[:output], language: opts[:language].to_sym, - namespace: opts[:namespace] + namespace: opts[:namespace], + options: { + rust_types_custom_str_impl: opts[:"rust-types-custom-str-impl"]&.split(',') || [], + }, ) compilation.compile end diff --git a/lib/xdrgen/compilation.rb b/lib/xdrgen/compilation.rb index baf8ba412..8b13cdf4f 100644 --- a/lib/xdrgen/compilation.rb +++ b/lib/xdrgen/compilation.rb @@ -2,11 +2,12 @@ module Xdrgen class Compilation extend Memoist - def initialize(source_paths, output_dir:".", language: :ruby, namespace: nil) + def initialize(source_paths, output_dir:".", language: :ruby, namespace: nil, options: {}) @source_paths = source_paths @output_dir = output_dir @namespace = namespace @language = language + @options = options end memoize def source @@ -22,10 +23,10 @@ def compile output = Output.new(@source_paths, @output_dir) - generator = Generators.for_language(@language).new(ast, output, @namespace) + generator = Generators.for_language(@language).new(ast, output, @namespace, @options) generator.generate ensure output.close end end -end \ No newline at end of file +end diff --git a/lib/xdrgen/generators/base.rb b/lib/xdrgen/generators/base.rb index c9d3ffad0..5a3a9e06b 100644 --- a/lib/xdrgen/generators/base.rb +++ b/lib/xdrgen/generators/base.rb @@ -1,11 +1,12 @@ class Xdrgen::Generators::Base - def initialize(top, output, namespace=nil) + def initialize(top, output, namespace=nil, options={}) @top = top @output = output @namespace = namespace + @options = options end def generate raise NotImplementedError end -end \ No newline at end of file +end diff --git a/lib/xdrgen/generators/go.rb b/lib/xdrgen/generators/go.rb index 16c9c6bb2..f2a6da035 100644 --- a/lib/xdrgen/generators/go.rb +++ b/lib/xdrgen/generators/go.rb @@ -536,7 +536,11 @@ def check_error(str) def render_struct_decode_from_interface(out, struct) name = name(struct) out.puts "// DecodeFrom decodes this value using the Decoder." - out.puts "func (s *#{name}) DecodeFrom(d *xdr.Decoder) (int, error) {" + out.puts "func (s *#{name}) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) {" + out.puts " if maxDepth == 0 {" + out.puts " return 0, fmt.Errorf(\"decoding #{name}: %w\", ErrMaxDecodingDepthReached)" + out.puts " }" + out.puts " maxDepth -= 1" out.puts " var err error" out.puts " var n, nTmp int" declared_variables = [] @@ -552,7 +556,11 @@ def render_struct_decode_from_interface(out, struct) def render_union_decode_from_interface(out, union) name = name(union) out.puts "// DecodeFrom decodes this value using the Decoder." - out.puts "func (u *#{name}) DecodeFrom(d *xdr.Decoder) (int, error) {" + out.puts "func (u *#{name}) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) {" + out.puts " if maxDepth == 0 {" + out.puts " return 0, fmt.Errorf(\"decoding #{name}: %w\", ErrMaxDecodingDepthReached)" + out.puts " }" + out.puts " maxDepth -= 1" out.puts " var err error" out.puts " var n, nTmp int" render_decode_from_body(out, "u.#{name(union.discriminant)}", union.discriminant.type, declared_variables: [], self_encode: false) @@ -581,10 +589,14 @@ def render_enum_decode_from_interface(out, typedef) type = typedef out.puts <<-EOS.strip_heredoc // DecodeFrom decodes this value using the Decoder. - func (e *#{name}) DecodeFrom(d *xdr.Decoder) (int, error) { + func (e *#{name}) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding #{name}: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding #{name}: %s", err) + return n, fmt.Errorf("decoding #{name}: %w", err) } if _, ok := #{private_name type}Map[v]; !ok { return n, fmt.Errorf("'%d' is not a valid #{name} enum value", v) @@ -599,7 +611,11 @@ def render_typedef_decode_from_interface(out, typedef) name = name(typedef) type = typedef.declaration.type out.puts "// DecodeFrom decodes this value using the Decoder." - out.puts "func (s *#{name}) DecodeFrom(d *xdr.Decoder) (int, error) {" + out.puts "func (s *#{name}) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) {" + out.puts " if maxDepth == 0 {" + out.puts " return 0, fmt.Errorf(\"decoding #{name}: %w\", ErrMaxDecodingDepthReached)" + out.puts " }" + out.puts " maxDepth -= 1" out.puts " var err error" out.puts " var n, nTmp int" var = "s" @@ -636,7 +652,7 @@ def render_decode_from_body(out, var, type, declared_variables:, self_encode:) tail = <<-EOS.strip_heredoc n += nTmp if err != nil { - return n, fmt.Errorf("decoding #{name type}: %s", err) + return n, fmt.Errorf("decoding #{name type}: %w", err) } EOS optional = type.sub_type == :optional @@ -692,7 +708,7 @@ def render_decode_from_body(out, var, type, declared_variables:, self_encode:) out.puts " #{var} = new(#{name type.resolved_type.declaration.type})" end var = "(*#{name type})(#{var})" if self_encode - out.puts " nTmp, err = #{var}.DecodeFrom(d)" + out.puts " nTmp, err = #{var}.DecodeFrom(d, maxDepth)" out.puts tail if optional_within out.puts " }" @@ -709,7 +725,7 @@ def render_decode_from_body(out, var, type, declared_variables:, self_encode:) out.puts " if eb {" var = "(*#{element_var})" end - out.puts " nTmp, err = #{element_var}.DecodeFrom(d)" + out.puts " nTmp, err = #{element_var}.DecodeFrom(d, maxDepth)" out.puts tail if optional_within out.puts " }" @@ -739,7 +755,7 @@ def render_decode_from_body(out, var, type, declared_variables:, self_encode:) out.puts " #{element_var} = new(#{name type.resolved_type.declaration.type})" var = "(*#{element_var})" end - out.puts " nTmp, err = #{element_var}.DecodeFrom(d)" + out.puts " nTmp, err = #{element_var}.DecodeFrom(d, maxDepth)" out.puts tail if optional_within out.puts " }" @@ -751,13 +767,13 @@ def render_decode_from_body(out, var, type, declared_variables:, self_encode:) end when AST::Definitions::Base if self_encode - out.puts " nTmp, err = #{name type}(#{var}).DecodeFrom(d)" + out.puts " nTmp, err = #{name type}(#{var}).DecodeFrom(d, maxDepth)" else - out.puts " nTmp, err = #{var}.DecodeFrom(d)" + out.puts " nTmp, err = #{var}.DecodeFrom(d, maxDepth)" end out.puts tail else - out.puts " nTmp, err = d.Decode(&#{var})" + out.puts " nTmp, err = d.DecodeWithMaxDepth(&#{var}, maxDepth)" out.puts tail end if optional @@ -778,7 +794,7 @@ def render_binary_interface(out, name) out.puts "func (s *#{name}) UnmarshalBinary(inp []byte) error {" out.puts " r := bytes.NewReader(inp)" out.puts " d := xdr.NewDecoder(r)" - out.puts " _, err := s.DecodeFrom(d)" + out.puts " _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth)" out.puts " return err" out.puts "}" out.break @@ -817,6 +833,7 @@ def render_top_matter(out) import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -832,19 +849,21 @@ def render_top_matter(out) EOS out.break out.puts <<-EOS.strip_heredoc + var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -979,7 +998,7 @@ def render_union_constructor(out, union) <<-EOS tv, ok := value.(#{reference arm.type}) if !ok { - err = fmt.Errorf("invalid value, must be #{reference arm.type}") + err = errors.New("invalid value, must be #{reference arm.type}") return } result.#{name arm} = &tv diff --git a/lib/xdrgen/generators/java.rb b/lib/xdrgen/generators/java.rb index 43eddb0f7..f8621674d 100644 --- a/lib/xdrgen/generators/java.rb +++ b/lib/xdrgen/generators/java.rb @@ -43,7 +43,7 @@ def render_definitions(node, constants_container) end def add_imports_for_definition(defn, imports) - imports.add("com.google.common.io.BaseEncoding") + imports.add("org.stellar.sdk.Base64Factory") imports.add("java.io.ByteArrayInputStream") imports.add("java.io.ByteArrayOutputStream") @@ -53,15 +53,15 @@ def add_imports_for_definition(defn, imports) if is_decl_array(m.declaration) imports.add('java.util.Arrays') else - imports.add('com.google.common.base.Objects') + imports.add('java.util.Objects') end end # if we have more than one member field then the # hash code will be computed by - # Objects.hashCode(field1, field2, ..., fieldN) - # therefore, we should always import com.google.common.base.Objects + # Objects.hash(field1, field2, ..., fieldN) + # therefore, we should always import java.util.Objects if defn.members.length > 1 - imports.add("com.google.common.base.Objects") + imports.add("java.util.Objects") end when AST::Definitions::Enum ; # no imports required for enums @@ -73,30 +73,30 @@ def add_imports_for_definition(defn, imports) if is_type_array(defn.discriminant.type) imports.add('java.util.Arrays') else - imports.add('com.google.common.base.Objects') + imports.add('java.util.Objects') end nonVoidArms.each do |a| if is_decl_array(a.declaration) imports.add('java.util.Arrays') else - imports.add('com.google.common.base.Objects') + imports.add('java.util.Objects') end end # if we have more than one field then the # hash code will be computed by - # Objects.hashCode(field1, field2, ..., fieldN) - # therefore, we should always import com.google.common.base.Objects + # Objects.hash(field1, field2, ..., fieldN) + # therefore, we should always import java.util.Objects # if we have more than one field if totalFields > 1 - imports.add("com.google.common.base.Objects") + imports.add("java.util.Objects") end when AST::Definitions::Typedef ; if is_decl_array(defn.declaration) imports.add('java.util.Arrays') else - imports.add('com.google.common.base.Objects') + imports.add('java.util.Objects') end end @@ -300,10 +300,10 @@ def render_struct(struct, out) if is_decl_array(struct.members[0].declaration) "Arrays.hashCode(this.#{struct.members[0].name})" else - "Objects.hashCode(this.#{struct.members[0].name})" + "Objects.hash(this.#{struct.members[0].name})" end else - "Objects.hashCode(#{ + "Objects.hash(#{ (struct.members.map { |m| if is_decl_array(m.declaration) "Arrays.hashCode(this.#{m.name})" @@ -324,7 +324,7 @@ def render_struct(struct, out) if is_decl_array(m.declaration) "Arrays.equals(this.#{m.name}, other.#{m.name})" else - "Objects.equal(this.#{m.name}, other.#{m.name})" + "Objects.equals(this.#{m.name}, other.#{m.name})" end } equalExpression = case equalParts.length @@ -432,7 +432,7 @@ def render_typedef(typedef, out) if is_decl_array(typedef.declaration) "Arrays.hashCode" else - "Objects.hashCode" + "Objects.hash" end out.puts <<-EOS.strip_heredoc @Override @@ -446,7 +446,7 @@ def render_typedef(typedef, out) if is_decl_array(typedef.declaration) "Arrays.equals" else - "Objects.equal" + "Objects.equals" end type = name_string typedef.name out.puts <<-EOS.strip_heredoc @@ -639,7 +639,7 @@ def render_union(union, out) } parts.append(discriminantPart) - hashCodeExpression = "Objects.hashCode(#{parts.join(", ")})" + hashCodeExpression = "Objects.hash(#{parts.join(", ")})" out.puts <<-EOS.strip_heredoc @Override public int hashCode() { @@ -651,14 +651,14 @@ def render_union(union, out) if is_decl_array(a.declaration) "Arrays.equals(this.#{a.name}, other.#{a.name})" else - "Objects.equal(this.#{a.name}, other.#{a.name})" + "Objects.equals(this.#{a.name}, other.#{a.name})" end } equalParts.append( if is_type_array(union.discriminant.type) "Arrays.equals(this.#{union.discriminant.name}, other.#{union.discriminant.name})" else - "Objects.equal(this.#{union.discriminant.name}, other.#{union.discriminant.name})" + "Objects.equals(this.#{union.discriminant.name}, other.#{union.discriminant.name})" end ) @@ -716,8 +716,7 @@ def render_base64(return_type, out) out.puts <<-EOS.strip_heredoc @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -729,8 +728,7 @@ def render_base64(return_type, out) } public static #{return_type} fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/lib/xdrgen/generators/java/XdrString.erb b/lib/xdrgen/generators/java/XdrString.erb index c56b62a16..bfad40c7f 100644 --- a/lib/xdrgen/generators/java/XdrString.erb +++ b/lib/xdrgen/generators/java/XdrString.erb @@ -1,12 +1,12 @@ package <%= @namespace %>; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public class XdrString implements XdrElement { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public class XdrString implements XdrElement { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/lib/xdrgen/generators/java/XdrUnsignedHyperInteger.erb b/lib/xdrgen/generators/java/XdrUnsignedHyperInteger.erb index c74d29f9f..6c05ca57d 100644 --- a/lib/xdrgen/generators/java/XdrUnsignedHyperInteger.erb +++ b/lib/xdrgen/generators/java/XdrUnsignedHyperInteger.erb @@ -1,12 +1,11 @@ -package <%= @namespace %>; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public class XdrUnsignedHyperInteger implements XdrElement { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public class XdrUnsignedHyperInteger implements XdrElement { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public class XdrUnsignedHyperInteger implements XdrElement { @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public class XdrUnsignedHyperInteger implements XdrElement { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/lib/xdrgen/generators/java/XdrUnsignedInteger.erb b/lib/xdrgen/generators/java/XdrUnsignedInteger.erb index 5b1056527..8f2a52395 100644 --- a/lib/xdrgen/generators/java/XdrUnsignedInteger.erb +++ b/lib/xdrgen/generators/java/XdrUnsignedInteger.erb @@ -1,11 +1,10 @@ -package <%= @namespace %>; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public class XdrUnsignedInteger implements XdrElement { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public class XdrUnsignedInteger implements XdrElement { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public class XdrUnsignedInteger implements XdrElement { @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public class XdrUnsignedInteger implements XdrElement { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/lib/xdrgen/generators/javascript.rb b/lib/xdrgen/generators/javascript.rb index f89691191..ef388346b 100644 --- a/lib/xdrgen/generators/javascript.rb +++ b/lib/xdrgen/generators/javascript.rb @@ -190,7 +190,12 @@ def const_name(named) end def member_name(member) - name(member).camelize(:lower) + fixedName = name(member).camelize(:lower) + # render set() as set_() because set is reserved by stellar/js-xdr + if fixedName == 'set' + return 'set_' + end + fixedName end def reference(type) diff --git a/lib/xdrgen/generators/rust.rb b/lib/xdrgen/generators/rust.rb index e388b156c..5352d2963 100644 --- a/lib/xdrgen/generators/rust.rb +++ b/lib/xdrgen/generators/rust.rb @@ -166,21 +166,21 @@ def render_enum_of_all_types(out, types) #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { #{types.map { |t| "TypeVariant::#{t} => r.with_limited_depth(|r| Ok(Self::#{t}(Box::new(#{t}::read_xdr(r)?))))," }.join("\n")} } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -192,52 +192,60 @@ def render_enum_of_all_types(out, types) } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} + #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, Frame<#{t}>>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::#{t}(Box::new(t.0)))))," }.join("\n")} + #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, Frame<#{t}>>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::#{t}(Box::new(t.0)))))," }.join("\n")} } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} + #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + #{types.map { |t| "TypeVariant::#{t} => Ok(Self::#{t}(Box::new(serde_json::from_reader(r)?)))," }.join("\n")} + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -283,6 +291,16 @@ def render_enum_of_all_types(out, types) Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + #{types.map { |t| "Self::#{t}(v) => v.write_xdr(w)," }.join("\n")} + } + } + } EOS out.break end @@ -344,7 +362,11 @@ def render_source_comment(out, defn) def render_struct(out, struct) out.puts "#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]" out.puts %{#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]} - out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + if @options[:rust_types_custom_str_impl].include?(name struct) + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]} + else + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + end out.puts "pub struct #{name struct} {" out.indent do struct.members.each do |m| @@ -356,7 +378,7 @@ def render_struct(out, struct) out.puts <<-EOS.strip_heredoc impl ReadXdr for #{name struct} { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ #{struct.members.map do |m| @@ -369,7 +391,7 @@ def render_struct(out, struct) impl WriteXdr for #{name struct} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { #{struct.members.map do |m| "self.#{field_name(m)}.write_xdr(w)?;" @@ -386,7 +408,11 @@ def render_enum(out, enum) out.puts "// enum" out.puts "#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]" out.puts %{#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]} - out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + if @options[:rust_types_custom_str_impl].include?(name enum) + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]} + else + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + end out.puts "#[repr(i32)]" out.puts "pub enum #{name enum} {" out.indent do @@ -459,7 +485,7 @@ def render_enum(out, enum) impl ReadXdr for #{name enum} { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -470,7 +496,7 @@ def render_enum(out, enum) impl WriteXdr for #{name enum} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -511,7 +537,11 @@ def render_union(out, union) out.puts "// union with discriminant #{discriminant_type}" out.puts "#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]" out.puts %{#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]} - out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + if @options[:rust_types_custom_str_impl].include?(name union) + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]} + else + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + end out.puts "#[allow(clippy::large_enum_variant)]" out.puts "pub enum #{name union} {" union_case_count = 0 @@ -591,7 +621,7 @@ def render_union(out, union) impl ReadXdr for #{name union} { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let dv: #{discriminant_type} = <#{discriminant_type} as ReadXdr>::read_xdr(r)?; #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] @@ -613,7 +643,7 @@ def render_union(out, union) impl WriteXdr for #{name union} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.discriminant().write_xdr(w)?; #[allow(clippy::match_same_arms)] @@ -641,34 +671,39 @@ def render_typedef(out, typedef) out.puts "#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]" out.puts %{#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]} out.puts "#[derive(Default)]" if is_var_array_type(typedef.type) - if is_fixed_array_opaque(typedef.type) - out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]} + if is_fixed_array_opaque(typedef.type) || @options[:rust_types_custom_str_impl].include?(name typedef) + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]} else - out.puts "#[derive(Debug)]" - out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + out.puts %{#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]} + end + if !is_fixed_array_opaque(typedef.type) + out.puts "#[derive(Debug)]" end out.puts "pub struct #{name typedef}(pub #{reference(typedef, typedef.type)});" out.puts "" if is_fixed_array_opaque(typedef.type) out.puts <<-EOS.strip_heredoc - impl core::fmt::Display for #{name typedef} { + impl core::fmt::Debug for #{name typedef} { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let v = &self.0; + write!(f, "#{name typedef}(")?; for b in v { write!(f, "{b:02x}")?; } + write!(f, ")")?; Ok(()) } } - - impl core::fmt::Debug for #{name typedef} { + EOS + end + if is_fixed_array_opaque(typedef.type) && !@options[:rust_types_custom_str_impl].include?(name typedef) + out.puts <<-EOS.strip_heredoc + impl core::fmt::Display for #{name typedef} { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let v = &self.0; - write!(f, "#{name typedef}(")?; for b in v { write!(f, "{b:02x}")?; } - write!(f, ")")?; Ok(()) } } @@ -706,7 +741,7 @@ def render_typedef(out, typedef) impl ReadXdr for #{name typedef} { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = #{reference_to_call(typedef, typedef.type)}::read_xdr(r)?; let v = #{name typedef}(i); @@ -717,7 +752,7 @@ def render_typedef(out, typedef) impl WriteXdr for #{name typedef} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } diff --git a/lib/xdrgen/generators/rust/src/types.rs b/lib/xdrgen/generators/rust/src/types.rs index 7d44d34c1..b64c54fb1 100644 --- a/lib/xdrgen/generators/rust/src/types.rs +++ b/lib/xdrgen/generators/rust/src/types.rs @@ -41,14 +41,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -65,6 +57,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -90,6 +85,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -109,6 +106,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -142,6 +142,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -180,148 +188,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - -#[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - #[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -335,17 +325,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -370,7 +360,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -407,17 +397,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -442,7 +432,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -458,10 +448,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -482,7 +472,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -506,7 +496,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -536,97 +526,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -638,9 +600,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -649,17 +612,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -668,17 +635,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -687,17 +658,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -706,43 +681,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -753,7 +731,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -763,7 +741,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -780,7 +758,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -795,39 +773,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -839,10 +820,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -850,7 +834,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -865,7 +849,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1198,17 +1182,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1221,14 +1209,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1237,14 +1229,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1257,7 +1249,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1596,17 +1588,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1619,11 +1615,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1979,17 +1979,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2002,14 +2006,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2033,7 +2041,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2056,34 +2064,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2093,8 +2093,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2106,11 +2105,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2118,34 +2114,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2155,7 +2146,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2166,10 +2157,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2178,10 +2166,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2218,19 +2203,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2240,19 +2224,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] diff --git a/lib/xdrgen/version.rb b/lib/xdrgen/version.rb index 38accfa39..ededcd7da 100644 --- a/lib/xdrgen/version.rb +++ b/lib/xdrgen/version.rb @@ -1,3 +1,3 @@ module Xdrgen - VERSION = "0.1.1" + VERSION = "0.1.3" end diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 32b8ac10a..631ef56bb 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -22,7 +22,7 @@ def generate(language, path) [path], output_dir: "#{SPEC_ROOT}/output/generator_spec_#{language}/#{File.basename path}", language: language, - namespace: "MyXDR" + namespace: "MyXDR", ) compilation.compile compilation diff --git a/spec/lib/xdrgen/rust_spec.rb b/spec/lib/xdrgen/rust_spec.rb new file mode 100644 index 000000000..1aa158b17 --- /dev/null +++ b/spec/lib/xdrgen/rust_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Xdrgen::Generators::Rust do + generator_fixture_paths.each do |path| + it "can generate #{File.basename path}" do + c = generate path, "" + end + + it "can generate #{File.basename path} with custom str impls" do + c = generate path, "_custom_str_impls", { + rust_types_custom_str_impl: [ + "Foo", + "TestArray", + "Color2", + "UnionKey", + "MyUnion", + "HasOptions", + "MyStruct", + "LotsOfMyStructs", + ], + } + end + end + + def generate(path, output_sub_path, options = {rust_types_custom_str_impl: []}) + compilation = Xdrgen::Compilation.new( + [path], + output_dir: "#{SPEC_ROOT}/output/generator_spec_rust#{output_sub_path}/#{File.basename path}", + language: "rust", + namespace: "MyXDR", + options: options, + ) + compilation.compile + compilation + end +end diff --git a/spec/output/generator_spec_go/block_comments.x/MyXDR_generated.go b/spec/output/generator_spec_go/block_comments.x/MyXDR_generated.go index b384c9a18..0eaa4ee67 100644 --- a/spec/output/generator_spec_go/block_comments.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/block_comments.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/block_comments.x": "e13131bc4134f38da17b9d5e9f67d2695a69ef98e3ef272833f4c18d0cc88a30", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -92,10 +95,14 @@ func (e AccountFlags) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*AccountFlags)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *AccountFlags) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *AccountFlags) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding AccountFlags: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding AccountFlags: %s", err) + return n, fmt.Errorf("decoding AccountFlags: %w", err) } if _, ok := accountFlagsMap[v]; !ok { return n, fmt.Errorf("'%d' is not a valid AccountFlags enum value", v) @@ -115,7 +122,7 @@ func (s AccountFlags) MarshalBinary() ([]byte, error) { func (s *AccountFlags) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_go/const.x/MyXDR_generated.go b/spec/output/generator_spec_go/const.x/MyXDR_generated.go index c884470c4..46a8aecff 100644 --- a/spec/output/generator_spec_go/const.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/const.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/const.x": "0bff3b37592fcc16cad2fe10b9a72f5d39d033a114917c24e86a9ebd9cda9c37", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -77,14 +80,18 @@ if _, err = e.EncodeInt(int32(s)); err != nil { var _ decoderFrom = (*TestArray)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *TestArray) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *TestArray) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding TestArray: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v [Foo]int32 v, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } *s = TestArray(v) return n, nil @@ -102,7 +109,7 @@ func (s TestArray) MarshalBinary() ([]byte, error) { func (s *TestArray) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -137,14 +144,18 @@ if _, err = e.EncodeInt(int32(s)); err != nil { var _ decoderFrom = (*TestArray2)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *TestArray2) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *TestArray2) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding TestArray2: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v []int32 v, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } *s = TestArray2(v) return n, nil @@ -162,7 +173,7 @@ func (s TestArray2) MarshalBinary() ([]byte, error) { func (s *TestArray2) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_go/enum.x/MyXDR_generated.go b/spec/output/generator_spec_go/enum.x/MyXDR_generated.go index b11df486b..592762bc4 100644 --- a/spec/output/generator_spec_go/enum.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/enum.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/enum.x": "35cf5e97e2057039640ed260e8b38bb2733a3c3ca8529c93877bdec02a999d7f", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -137,10 +140,14 @@ func (e MessageType) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*MessageType)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *MessageType) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *MessageType) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MessageType: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding MessageType: %s", err) + return n, fmt.Errorf("decoding MessageType: %w", err) } if _, ok := messageTypeMap[v]; !ok { return n, fmt.Errorf("'%d' is not a valid MessageType enum value", v) @@ -160,7 +167,7 @@ func (s MessageType) MarshalBinary() ([]byte, error) { func (s *MessageType) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -217,10 +224,14 @@ func (e Color) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*Color)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *Color) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *Color) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Color: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding Color: %s", err) + return n, fmt.Errorf("decoding Color: %w", err) } if _, ok := colorMap[v]; !ok { return n, fmt.Errorf("'%d' is not a valid Color enum value", v) @@ -240,7 +251,7 @@ func (s Color) MarshalBinary() ([]byte, error) { func (s *Color) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -297,10 +308,14 @@ func (e Color2) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*Color2)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *Color2) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *Color2) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Color2: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding Color2: %s", err) + return n, fmt.Errorf("decoding Color2: %w", err) } if _, ok := color2Map[v]; !ok { return n, fmt.Errorf("'%d' is not a valid Color2 enum value", v) @@ -320,7 +335,7 @@ func (s Color2) MarshalBinary() ([]byte, error) { func (s *Color2) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_go/nesting.x/MyXDR_generated.go b/spec/output/generator_spec_go/nesting.x/MyXDR_generated.go index d353ad983..7f8219c92 100644 --- a/spec/output/generator_spec_go/nesting.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/nesting.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/nesting.x": "5537949272c11f1bd09cf613a3751668b5018d686a1c2aaa3baa91183ca18f6a", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -97,10 +100,14 @@ func (e UnionKey) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*UnionKey)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *UnionKey) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *UnionKey) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding UnionKey: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding UnionKey: %s", err) + return n, fmt.Errorf("decoding UnionKey: %w", err) } if _, ok := unionKeyMap[v]; !ok { return n, fmt.Errorf("'%d' is not a valid UnionKey enum value", v) @@ -120,7 +127,7 @@ func (s UnionKey) MarshalBinary() ([]byte, error) { func (s *UnionKey) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -152,14 +159,18 @@ if _, err = e.EncodeInt(int32(s)); err != nil { var _ decoderFrom = (*Foo)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Foo) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Foo) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Foo: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v int32 v, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } *s = Foo(v) return n, nil @@ -177,7 +188,7 @@ func (s Foo) MarshalBinary() ([]byte, error) { func (s *Foo) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -213,13 +224,17 @@ if _, err = e.EncodeInt(int32(s.SomeInt)); err != nil { var _ decoderFrom = (*MyUnionOne)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *MyUnionOne) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *MyUnionOne) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyUnionOne: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int s.SomeInt, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } return n, nil } @@ -236,7 +251,7 @@ func (s MyUnionOne) MarshalBinary() ([]byte, error) { func (s *MyUnionOne) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -277,18 +292,22 @@ if err = s.Foo.EncodeTo(e); err != nil { var _ decoderFrom = (*MyUnionTwo)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *MyUnionTwo) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *MyUnionTwo) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyUnionTwo: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int s.SomeInt, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } - nTmp, err = s.Foo.DecodeFrom(d) + nTmp, err = s.Foo.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Foo: %s", err) + return n, fmt.Errorf("decoding Foo: %w", err) } return n, nil } @@ -305,7 +324,7 @@ func (s MyUnionTwo) MarshalBinary() ([]byte, error) { func (s *MyUnionTwo) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -372,14 +391,14 @@ switch UnionKey(aType) { case UnionKeyOne: tv, ok := value.(MyUnionOne) if !ok { - err = fmt.Errorf("invalid value, must be MyUnionOne") + err = errors.New("invalid value, must be MyUnionOne") return } result.One = &tv case UnionKeyTwo: tv, ok := value.(MyUnionTwo) if !ok { - err = fmt.Errorf("invalid value, must be MyUnionTwo") + err = errors.New("invalid value, must be MyUnionTwo") return } result.Two = &tv @@ -463,29 +482,33 @@ return nil var _ decoderFrom = (*MyUnion)(nil) // DecodeFrom decodes this value using the Decoder. -func (u *MyUnion) DecodeFrom(d *xdr.Decoder) (int, error) { +func (u *MyUnion) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyUnion: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int - nTmp, err = u.Type.DecodeFrom(d) + nTmp, err = u.Type.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding UnionKey: %s", err) + return n, fmt.Errorf("decoding UnionKey: %w", err) } switch UnionKey(u.Type) { case UnionKeyOne: u.One = new(MyUnionOne) - nTmp, err = (*u.One).DecodeFrom(d) + nTmp, err = (*u.One).DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding MyUnionOne: %s", err) + return n, fmt.Errorf("decoding MyUnionOne: %w", err) } return n, nil case UnionKeyTwo: u.Two = new(MyUnionTwo) - nTmp, err = (*u.Two).DecodeFrom(d) + nTmp, err = (*u.Two).DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding MyUnionTwo: %s", err) + return n, fmt.Errorf("decoding MyUnionTwo: %w", err) } return n, nil case UnionKeyOffer: @@ -507,7 +530,7 @@ func (s MyUnion) MarshalBinary() ([]byte, error) { func (s *MyUnion) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_go/optional.x/MyXDR_generated.go b/spec/output/generator_spec_go/optional.x/MyXDR_generated.go index 0f8db98de..cb030c26d 100644 --- a/spec/output/generator_spec_go/optional.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/optional.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/optional.x": "3241e832fcf00bca4315ecb6c259621dafb0e302a63a993f5504b0b5cebb6bd7", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -71,14 +74,18 @@ if _, err = e.EncodeInt(int32(s)); err != nil { var _ decoderFrom = (*Arr)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Arr) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Arr) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Arr: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v [2]int32 v, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } *s = Arr(v) return n, nil @@ -96,7 +103,7 @@ func (s Arr) MarshalBinary() ([]byte, error) { func (s *Arr) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -158,14 +165,18 @@ if err = (*s.ThirdOption).EncodeTo(e); err != nil { var _ decoderFrom = (*HasOptions)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *HasOptions) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *HasOptions) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding HasOptions: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var b bool b, nTmp, err = d.DecodeBool() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } s.FirstOption = nil if b { @@ -173,13 +184,13 @@ if err != nil { s.FirstOption, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } } b, nTmp, err = d.DecodeBool() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } s.SecondOption = nil if b { @@ -187,21 +198,21 @@ if err != nil { s.SecondOption, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } } b, nTmp, err = d.DecodeBool() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Arr: %s", err) + return n, fmt.Errorf("decoding Arr: %w", err) } s.ThirdOption = nil if b { s.ThirdOption = new(Arr) - nTmp, err = s.ThirdOption.DecodeFrom(d) + nTmp, err = s.ThirdOption.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Arr: %s", err) + return n, fmt.Errorf("decoding Arr: %w", err) } } return n, nil @@ -219,7 +230,7 @@ func (s HasOptions) MarshalBinary() ([]byte, error) { func (s *HasOptions) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_go/struct.x/MyXDR_generated.go b/spec/output/generator_spec_go/struct.x/MyXDR_generated.go index 9f4c1cb73..b2f7d834b 100644 --- a/spec/output/generator_spec_go/struct.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/struct.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/struct.x": "c6911a83390e3b499c078fd0c579132eacce88a4a0538d3b8b5e57747a58db4a", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -72,14 +75,18 @@ if _, err = e.EncodeHyper(int64(s)); err != nil { var _ decoderFrom = (*Int64)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Int64) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Int64) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Int64: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v int64 v, nTmp, err = d.DecodeHyper() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hyper: %s", err) + return n, fmt.Errorf("decoding Hyper: %w", err) } *s = Int64(v) return n, nil @@ -97,7 +104,7 @@ func (s Int64) MarshalBinary() ([]byte, error) { func (s *Int64) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -154,33 +161,37 @@ if _, err = e.EncodeString(string(s.MaxString)); err != nil { var _ decoderFrom = (*MyStruct)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *MyStruct) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *MyStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int s.SomeInt, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } - nTmp, err = s.ABigInt.DecodeFrom(d) + nTmp, err = s.ABigInt.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int64: %s", err) + return n, fmt.Errorf("decoding Int64: %w", err) } nTmp, err = d.DecodeFixedOpaqueInplace(s.SomeOpaque[:]) n += nTmp if err != nil { - return n, fmt.Errorf("decoding SomeOpaque: %s", err) + return n, fmt.Errorf("decoding SomeOpaque: %w", err) } s.SomeString, nTmp, err = d.DecodeString(0) n += nTmp if err != nil { - return n, fmt.Errorf("decoding SomeString: %s", err) + return n, fmt.Errorf("decoding SomeString: %w", err) } s.MaxString, nTmp, err = d.DecodeString(100) n += nTmp if err != nil { - return n, fmt.Errorf("decoding MaxString: %s", err) + return n, fmt.Errorf("decoding MaxString: %w", err) } return n, nil } @@ -197,7 +208,7 @@ func (s MyStruct) MarshalBinary() ([]byte, error) { func (s *MyStruct) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_go/test.x/MyXDR_generated.go b/spec/output/generator_spec_go/test.x/MyXDR_generated.go index 62ac4168c..0bd57e8fb 100644 --- a/spec/output/generator_spec_go/test.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/test.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/test.x": "d29a98a6a3b9bf533a3e6712d928e0bed655e0f462ac4dae810c65d52ca9af41", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -76,13 +79,17 @@ if _, err = e.EncodeFixedOpaque(s[:]); err != nil { var _ decoderFrom = (*Uint512)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Uint512) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Uint512) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Uint512: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int nTmp, err = d.DecodeFixedOpaqueInplace(s[:]) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Uint512: %s", err) + return n, fmt.Errorf("decoding Uint512: %w", err) } return n, nil } @@ -99,7 +106,7 @@ func (s Uint512) MarshalBinary() ([]byte, error) { func (s *Uint512) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -135,13 +142,17 @@ if _, err = e.EncodeOpaque(s[:]); err != nil { var _ decoderFrom = (*Uint513)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Uint513) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Uint513) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Uint513: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int (*s), nTmp, err = d.DecodeOpaque(64) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Uint513: %s", err) + return n, fmt.Errorf("decoding Uint513: %w", err) } return n, nil } @@ -158,7 +169,7 @@ func (s Uint513) MarshalBinary() ([]byte, error) { func (s *Uint513) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -190,13 +201,17 @@ if _, err = e.EncodeOpaque(s[:]); err != nil { var _ decoderFrom = (*Uint514)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Uint514) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Uint514) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Uint514: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int (*s), nTmp, err = d.DecodeOpaque(0) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Uint514: %s", err) + return n, fmt.Errorf("decoding Uint514: %w", err) } return n, nil } @@ -213,7 +228,7 @@ func (s Uint514) MarshalBinary() ([]byte, error) { func (s *Uint514) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -249,14 +264,18 @@ if _, err = e.EncodeString(string(s)); err != nil { var _ decoderFrom = (*Str)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Str) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Str) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Str: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v string v, nTmp, err = d.DecodeString(64) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Str: %s", err) + return n, fmt.Errorf("decoding Str: %w", err) } *s = Str(v) return n, nil @@ -274,7 +293,7 @@ func (s Str) MarshalBinary() ([]byte, error) { func (s *Str) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -306,14 +325,18 @@ if _, err = e.EncodeString(string(s)); err != nil { var _ decoderFrom = (*Str2)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Str2) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Str2) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Str2: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v string v, nTmp, err = d.DecodeString(0) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Str2: %s", err) + return n, fmt.Errorf("decoding Str2: %w", err) } *s = Str2(v) return n, nil @@ -331,7 +354,7 @@ func (s Str2) MarshalBinary() ([]byte, error) { func (s *Str2) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -367,13 +390,17 @@ if _, err = e.EncodeFixedOpaque(s[:]); err != nil { var _ decoderFrom = (*Hash)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Hash) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Hash) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Hash: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int nTmp, err = d.DecodeFixedOpaqueInplace(s[:]) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hash: %s", err) + return n, fmt.Errorf("decoding Hash: %w", err) } return n, nil } @@ -390,7 +417,7 @@ func (s Hash) MarshalBinary() ([]byte, error) { func (s *Hash) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -423,14 +450,18 @@ if err = s[i].EncodeTo(e); err != nil { var _ decoderFrom = (*Hashes1)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Hashes1) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Hashes1) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Hashes1: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int for i := 0; i < len(s); i++ { - nTmp, err = s[i].DecodeFrom(d) + nTmp, err = s[i].DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hash: %s", err) + return n, fmt.Errorf("decoding Hash: %w", err) } } return n, nil @@ -448,7 +479,7 @@ func (s Hashes1) MarshalBinary() ([]byte, error) { func (s *Hashes1) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -488,14 +519,18 @@ if err = s[i].EncodeTo(e); err != nil { var _ decoderFrom = (*Hashes2)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Hashes2) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Hashes2) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Hashes2: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var l uint32 l, nTmp, err = d.DecodeUint() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hash: %s", err) + return n, fmt.Errorf("decoding Hash: %w", err) } if l > 12 { return n, fmt.Errorf("decoding Hash: data size (%d) exceeds size limit (12)", l) @@ -504,10 +539,10 @@ if err != nil { if l > 0 { (*s) = make([]Hash, l) for i := uint32(0); i < l; i++ { - nTmp, err = (*s)[i].DecodeFrom(d) + nTmp, err = (*s)[i].DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hash: %s", err) + return n, fmt.Errorf("decoding Hash: %w", err) } } } @@ -526,7 +561,7 @@ func (s Hashes2) MarshalBinary() ([]byte, error) { func (s *Hashes2) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -562,23 +597,27 @@ if err = s[i].EncodeTo(e); err != nil { var _ decoderFrom = (*Hashes3)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Hashes3) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Hashes3) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Hashes3: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var l uint32 l, nTmp, err = d.DecodeUint() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hash: %s", err) + return n, fmt.Errorf("decoding Hash: %w", err) } (*s) = nil if l > 0 { (*s) = make([]Hash, l) for i := uint32(0); i < l; i++ { - nTmp, err = (*s)[i].DecodeFrom(d) + nTmp, err = (*s)[i].DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hash: %s", err) + return n, fmt.Errorf("decoding Hash: %w", err) } } } @@ -597,7 +636,7 @@ func (s Hashes3) MarshalBinary() ([]byte, error) { func (s *Hashes3) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -639,14 +678,18 @@ if _, err = e.EncodeInt(int32(s)); err != nil { var _ decoderFrom = (*Int1)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Int1) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Int1) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Int1: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v int32 v, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } *s = Int1(v) return n, nil @@ -664,7 +707,7 @@ func (s Int1) MarshalBinary() ([]byte, error) { func (s *Int1) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -696,14 +739,18 @@ if _, err = e.EncodeHyper(int64(s)); err != nil { var _ decoderFrom = (*Int2)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Int2) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Int2) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Int2: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v int64 v, nTmp, err = d.DecodeHyper() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Hyper: %s", err) + return n, fmt.Errorf("decoding Hyper: %w", err) } *s = Int2(v) return n, nil @@ -721,7 +768,7 @@ func (s Int2) MarshalBinary() ([]byte, error) { func (s *Int2) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -753,14 +800,18 @@ if _, err = e.EncodeUint(uint32(s)); err != nil { var _ decoderFrom = (*Int3)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Int3) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Int3) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Int3: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v uint32 v, nTmp, err = d.DecodeUint() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Unsigned int: %s", err) + return n, fmt.Errorf("decoding Unsigned int: %w", err) } *s = Int3(v) return n, nil @@ -778,7 +829,7 @@ func (s Int3) MarshalBinary() ([]byte, error) { func (s *Int3) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -810,14 +861,18 @@ if _, err = e.EncodeUhyper(uint64(s)); err != nil { var _ decoderFrom = (*Int4)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Int4) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Int4) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Int4: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v uint64 v, nTmp, err = d.DecodeUhyper() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Unsigned hyper: %s", err) + return n, fmt.Errorf("decoding Unsigned hyper: %w", err) } *s = Int4(v) return n, nil @@ -835,7 +890,7 @@ func (s Int4) MarshalBinary() ([]byte, error) { func (s *Int4) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -907,53 +962,57 @@ if _, err = e.EncodeBool(bool(s.Field7)); err != nil { var _ decoderFrom = (*MyStruct)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *MyStruct) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *MyStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int - nTmp, err = s.Field1.DecodeFrom(d) + nTmp, err = s.Field1.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Uint512: %s", err) + return n, fmt.Errorf("decoding Uint512: %w", err) } var b bool b, nTmp, err = d.DecodeBool() n += nTmp if err != nil { - return n, fmt.Errorf("decoding OptHash1: %s", err) + return n, fmt.Errorf("decoding OptHash1: %w", err) } s.Field2 = nil if b { s.Field2 = new(Hash) - nTmp, err = s.Field2.DecodeFrom(d) + nTmp, err = s.Field2.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding OptHash1: %s", err) + return n, fmt.Errorf("decoding OptHash1: %w", err) } } - nTmp, err = s.Field3.DecodeFrom(d) + nTmp, err = s.Field3.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int1: %s", err) + return n, fmt.Errorf("decoding Int1: %w", err) } s.Field4, nTmp, err = d.DecodeUint() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Unsigned int: %s", err) + return n, fmt.Errorf("decoding Unsigned int: %w", err) } - nTmp, err = d.Decode(&s.Field5) + nTmp, err = d.DecodeWithMaxDepth(&s.Field5, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Float: %s", err) + return n, fmt.Errorf("decoding Float: %w", err) } - nTmp, err = d.Decode(&s.Field6) + nTmp, err = d.DecodeWithMaxDepth(&s.Field6, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Double: %s", err) + return n, fmt.Errorf("decoding Double: %w", err) } s.Field7, nTmp, err = d.DecodeBool() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Bool: %s", err) + return n, fmt.Errorf("decoding Bool: %w", err) } return n, nil } @@ -970,7 +1029,7 @@ func (s MyStruct) MarshalBinary() ([]byte, error) { func (s *MyStruct) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -1012,23 +1071,27 @@ if err = s.Members[i].EncodeTo(e); err != nil { var _ decoderFrom = (*LotsOfMyStructs)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *LotsOfMyStructs) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *LotsOfMyStructs) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding LotsOfMyStructs: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var l uint32 l, nTmp, err = d.DecodeUint() n += nTmp if err != nil { - return n, fmt.Errorf("decoding MyStruct: %s", err) + return n, fmt.Errorf("decoding MyStruct: %w", err) } s.Members = nil if l > 0 { s.Members = make([]MyStruct, l) for i := uint32(0); i < l; i++ { - nTmp, err = s.Members[i].DecodeFrom(d) + nTmp, err = s.Members[i].DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding MyStruct: %s", err) + return n, fmt.Errorf("decoding MyStruct: %w", err) } } } @@ -1047,7 +1110,7 @@ func (s LotsOfMyStructs) MarshalBinary() ([]byte, error) { func (s *LotsOfMyStructs) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -1084,13 +1147,17 @@ if err = s.Data.EncodeTo(e); err != nil { var _ decoderFrom = (*HasStuff)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *HasStuff) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *HasStuff) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding HasStuff: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int - nTmp, err = s.Data.DecodeFrom(d) + nTmp, err = s.Data.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding LotsOfMyStructs: %s", err) + return n, fmt.Errorf("decoding LotsOfMyStructs: %w", err) } return n, nil } @@ -1107,7 +1174,7 @@ func (s HasStuff) MarshalBinary() ([]byte, error) { func (s *HasStuff) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -1164,10 +1231,14 @@ func (e Color) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*Color)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *Color) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *Color) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Color: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding Color: %s", err) + return n, fmt.Errorf("decoding Color: %w", err) } if _, ok := colorMap[v]; !ok { return n, fmt.Errorf("'%d' is not a valid Color enum value", v) @@ -1187,7 +1258,7 @@ func (s Color) MarshalBinary() ([]byte, error) { func (s *Color) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -1253,10 +1324,14 @@ func (e NesterNestedEnum) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*NesterNestedEnum)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *NesterNestedEnum) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *NesterNestedEnum) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding NesterNestedEnum: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding NesterNestedEnum: %s", err) + return n, fmt.Errorf("decoding NesterNestedEnum: %w", err) } if _, ok := nestedEnumMap[v]; !ok { return n, fmt.Errorf("'%d' is not a valid NesterNestedEnum enum value", v) @@ -1276,7 +1351,7 @@ func (s NesterNestedEnum) MarshalBinary() ([]byte, error) { func (s *NesterNestedEnum) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -1312,13 +1387,17 @@ if _, err = e.EncodeInt(int32(s.Blah)); err != nil { var _ decoderFrom = (*NesterNestedStruct)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *NesterNestedStruct) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *NesterNestedStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding NesterNestedStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int s.Blah, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } return n, nil } @@ -1335,7 +1414,7 @@ func (s NesterNestedStruct) MarshalBinary() ([]byte, error) { func (s *NesterNestedStruct) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -1390,7 +1469,7 @@ switch Color(color) { default: tv, ok := value.(int32) if !ok { - err = fmt.Errorf("invalid value, must be int32") + err = errors.New("invalid value, must be int32") return } result.Blah2 = &tv @@ -1442,13 +1521,17 @@ return nil var _ decoderFrom = (*NesterNestedUnion)(nil) // DecodeFrom decodes this value using the Decoder. -func (u *NesterNestedUnion) DecodeFrom(d *xdr.Decoder) (int, error) { +func (u *NesterNestedUnion) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding NesterNestedUnion: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int - nTmp, err = u.Color.DecodeFrom(d) + nTmp, err = u.Color.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Color: %s", err) + return n, fmt.Errorf("decoding Color: %w", err) } switch Color(u.Color) { case ColorRed: @@ -1459,7 +1542,7 @@ switch Color(u.Color) { (*u.Blah2), nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } return n, nil } @@ -1477,7 +1560,7 @@ func (s NesterNestedUnion) MarshalBinary() ([]byte, error) { func (s *NesterNestedUnion) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -1538,23 +1621,27 @@ if err = s.NestedUnion.EncodeTo(e); err != nil { var _ decoderFrom = (*Nester)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Nester) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Nester) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Nester: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int - nTmp, err = s.NestedEnum.DecodeFrom(d) + nTmp, err = s.NestedEnum.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding NesterNestedEnum: %s", err) + return n, fmt.Errorf("decoding NesterNestedEnum: %w", err) } - nTmp, err = s.NestedStruct.DecodeFrom(d) + nTmp, err = s.NestedStruct.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding NesterNestedStruct: %s", err) + return n, fmt.Errorf("decoding NesterNestedStruct: %w", err) } - nTmp, err = s.NestedUnion.DecodeFrom(d) + nTmp, err = s.NestedUnion.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding NesterNestedUnion: %s", err) + return n, fmt.Errorf("decoding NesterNestedUnion: %w", err) } return n, nil } @@ -1571,7 +1658,7 @@ func (s Nester) MarshalBinary() ([]byte, error) { func (s *Nester) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_go/union.x/MyXDR_generated.go b/spec/output/generator_spec_go/union.x/MyXDR_generated.go index c15e8edc1..980ddd3f8 100644 --- a/spec/output/generator_spec_go/union.x/MyXDR_generated.go +++ b/spec/output/generator_spec_go/union.x/MyXDR_generated.go @@ -11,6 +11,7 @@ package MyXDR import ( "bytes" "encoding" + "errors" "io" "fmt" @@ -22,19 +23,21 @@ var XdrFilesSHA256 = map[string]string{ "spec/fixtures/generator/union.x": "c251258d967223b341ebcf2d5bb0718e9a039b46232cb743865d9acd0c4bbe41", } +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + type xdrType interface { xdrType() } type decoderFrom interface { - DecodeFrom(d *xdr.Decoder) (int, error) + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) } // Unmarshal reads an xdr element from `r` into `v`. func Unmarshal(r io.Reader, v interface{}) (int, error) { if decodable, ok := v.(decoderFrom); ok { d := xdr.NewDecoder(r) - return decodable.DecodeFrom(d) + return decodable.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) } // delegate to xdr package's Unmarshal return xdr.Unmarshal(r, v) @@ -72,14 +75,18 @@ if _, err = e.EncodeInt(int32(s)); err != nil { var _ decoderFrom = (*Error)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Error) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Error) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Error: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v int32 v, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } *s = Error(v) return n, nil @@ -97,7 +104,7 @@ func (s Error) MarshalBinary() ([]byte, error) { func (s *Error) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -129,14 +136,18 @@ if _, err = e.EncodeInt(int32(s)); err != nil { var _ decoderFrom = (*Multi)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *Multi) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *Multi) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding Multi: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int var v int32 v, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } *s = Multi(v) return n, nil @@ -154,7 +165,7 @@ func (s Multi) MarshalBinary() ([]byte, error) { func (s *Multi) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -208,10 +219,14 @@ func (e UnionKey) EncodeTo(enc *xdr.Encoder) error { } var _ decoderFrom = (*UnionKey)(nil) // DecodeFrom decodes this value using the Decoder. -func (e *UnionKey) DecodeFrom(d *xdr.Decoder) (int, error) { +func (e *UnionKey) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding UnionKey: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 v, n, err := d.DecodeInt() if err != nil { - return n, fmt.Errorf("decoding UnionKey: %s", err) + return n, fmt.Errorf("decoding UnionKey: %w", err) } if _, ok := unionKeyMap[v]; !ok { return n, fmt.Errorf("'%d' is not a valid UnionKey enum value", v) @@ -231,7 +246,7 @@ func (s UnionKey) MarshalBinary() ([]byte, error) { func (s *UnionKey) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -289,14 +304,14 @@ switch UnionKey(aType) { case UnionKeyError: tv, ok := value.(Error) if !ok { - err = fmt.Errorf("invalid value, must be Error") + err = errors.New("invalid value, must be Error") return } result.Error = &tv case UnionKeyMulti: tv, ok := value.([]Multi) if !ok { - err = fmt.Errorf("invalid value, must be []Multi") + err = errors.New("invalid value, must be []Multi") return } result.Things = &tv @@ -380,21 +395,25 @@ return nil var _ decoderFrom = (*MyUnion)(nil) // DecodeFrom decodes this value using the Decoder. -func (u *MyUnion) DecodeFrom(d *xdr.Decoder) (int, error) { +func (u *MyUnion) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyUnion: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int - nTmp, err = u.Type.DecodeFrom(d) + nTmp, err = u.Type.DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding UnionKey: %s", err) + return n, fmt.Errorf("decoding UnionKey: %w", err) } switch UnionKey(u.Type) { case UnionKeyError: u.Error = new(Error) - nTmp, err = (*u.Error).DecodeFrom(d) + nTmp, err = (*u.Error).DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Error: %s", err) + return n, fmt.Errorf("decoding Error: %w", err) } return n, nil case UnionKeyMulti: @@ -403,16 +422,16 @@ if err != nil { l, nTmp, err = d.DecodeUint() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Multi: %s", err) + return n, fmt.Errorf("decoding Multi: %w", err) } (*u.Things) = nil if l > 0 { (*u.Things) = make([]Multi, l) for i := uint32(0); i < l; i++ { - nTmp, err = (*u.Things)[i].DecodeFrom(d) + nTmp, err = (*u.Things)[i].DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Multi: %s", err) + return n, fmt.Errorf("decoding Multi: %w", err) } } } @@ -433,7 +452,7 @@ func (s MyUnion) MarshalBinary() ([]byte, error) { func (s *MyUnion) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -490,14 +509,14 @@ switch int32(aType) { case 0: tv, ok := value.(Error) if !ok { - err = fmt.Errorf("invalid value, must be Error") + err = errors.New("invalid value, must be Error") return } result.Error = &tv case 1: tv, ok := value.([]Multi) if !ok { - err = fmt.Errorf("invalid value, must be []Multi") + err = errors.New("invalid value, must be []Multi") return } result.Things = &tv @@ -581,21 +600,25 @@ return nil var _ decoderFrom = (*IntUnion)(nil) // DecodeFrom decodes this value using the Decoder. -func (u *IntUnion) DecodeFrom(d *xdr.Decoder) (int, error) { +func (u *IntUnion) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding IntUnion: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int u.Type, nTmp, err = d.DecodeInt() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Int: %s", err) + return n, fmt.Errorf("decoding Int: %w", err) } switch int32(u.Type) { case 0: u.Error = new(Error) - nTmp, err = (*u.Error).DecodeFrom(d) + nTmp, err = (*u.Error).DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Error: %s", err) + return n, fmt.Errorf("decoding Error: %w", err) } return n, nil case 1: @@ -604,16 +627,16 @@ if err != nil { l, nTmp, err = d.DecodeUint() n += nTmp if err != nil { - return n, fmt.Errorf("decoding Multi: %s", err) + return n, fmt.Errorf("decoding Multi: %w", err) } (*u.Things) = nil if l > 0 { (*u.Things) = make([]Multi, l) for i := uint32(0); i < l; i++ { - nTmp, err = (*u.Things)[i].DecodeFrom(d) + nTmp, err = (*u.Things)[i].DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding Multi: %s", err) + return n, fmt.Errorf("decoding Multi: %w", err) } } } @@ -634,7 +657,7 @@ func (s IntUnion) MarshalBinary() ([]byte, error) { func (s *IntUnion) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } @@ -707,13 +730,17 @@ if err = IntUnion(s).EncodeTo(e); err != nil { var _ decoderFrom = (*IntUnion2)(nil) // DecodeFrom decodes this value using the Decoder. -func (s *IntUnion2) DecodeFrom(d *xdr.Decoder) (int, error) { +func (s *IntUnion2) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding IntUnion2: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 var err error var n, nTmp int - nTmp, err = (*IntUnion)(s).DecodeFrom(d) + nTmp, err = (*IntUnion)(s).DecodeFrom(d, maxDepth) n += nTmp if err != nil { - return n, fmt.Errorf("decoding IntUnion: %s", err) + return n, fmt.Errorf("decoding IntUnion: %w", err) } return n, nil } @@ -730,7 +757,7 @@ func (s IntUnion2) MarshalBinary() ([]byte, error) { func (s *IntUnion2) UnmarshalBinary(inp []byte) error { r := bytes.NewReader(inp) d := xdr.NewDecoder(r) - _, err := s.DecodeFrom(d) + _, err := s.DecodeFrom(d, xdr.DecodeDefaultMaxDepth) return err } diff --git a/spec/output/generator_spec_java/block_comments.x/AccountFlags.java b/spec/output/generator_spec_java/block_comments.x/AccountFlags.java index d0b607443..2b4fee010 100644 --- a/spec/output/generator_spec_java/block_comments.x/AccountFlags.java +++ b/spec/output/generator_spec_java/block_comments.x/AccountFlags.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -49,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -62,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static AccountFlags fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/block_comments.x/XdrString.java b/spec/output/generator_spec_java/block_comments.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/block_comments.x/XdrString.java +++ b/spec/output/generator_spec_java/block_comments.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/block_comments.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/block_comments.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/block_comments.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/block_comments.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/block_comments.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/block_comments.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/block_comments.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/block_comments.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/const.x/TestArray.java b/spec/output/generator_spec_java/const.x/TestArray.java index 00f31b454..684dcacaf 100644 --- a/spec/output/generator_spec_java/const.x/TestArray.java +++ b/spec/output/generator_spec_java/const.x/TestArray.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -69,8 +69,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -82,8 +81,7 @@ public byte[] toXdrByteArray() throws IOException { } public static TestArray fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/const.x/TestArray2.java b/spec/output/generator_spec_java/const.x/TestArray2.java index 7be8cb5a7..887d18210 100644 --- a/spec/output/generator_spec_java/const.x/TestArray2.java +++ b/spec/output/generator_spec_java/const.x/TestArray2.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -70,8 +70,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -83,8 +82,7 @@ public byte[] toXdrByteArray() throws IOException { } public static TestArray2 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/const.x/XdrString.java b/spec/output/generator_spec_java/const.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/const.x/XdrString.java +++ b/spec/output/generator_spec_java/const.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/const.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/const.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/const.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/const.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/const.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/const.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/const.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/const.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/enum.x/Color.java b/spec/output/generator_spec_java/enum.x/Color.java index 147d42a4b..9e9c6e66c 100644 --- a/spec/output/generator_spec_java/enum.x/Color.java +++ b/spec/output/generator_spec_java/enum.x/Color.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -54,8 +54,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -67,8 +66,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Color fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/enum.x/Color2.java b/spec/output/generator_spec_java/enum.x/Color2.java index 855462321..8232789a9 100644 --- a/spec/output/generator_spec_java/enum.x/Color2.java +++ b/spec/output/generator_spec_java/enum.x/Color2.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -54,8 +54,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -67,8 +66,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Color2 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/enum.x/MessageType.java b/spec/output/generator_spec_java/enum.x/MessageType.java index 9540adde0..53dbf9ddf 100644 --- a/spec/output/generator_spec_java/enum.x/MessageType.java +++ b/spec/output/generator_spec_java/enum.x/MessageType.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -94,8 +94,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -107,8 +106,7 @@ public byte[] toXdrByteArray() throws IOException { } public static MessageType fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/enum.x/XdrString.java b/spec/output/generator_spec_java/enum.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/enum.x/XdrString.java +++ b/spec/output/generator_spec_java/enum.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/enum.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/enum.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/enum.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/enum.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/enum.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/enum.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/enum.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/enum.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/nesting.x/Foo.java b/spec/output/generator_spec_java/nesting.x/Foo.java index 706124313..134c2ba45 100644 --- a/spec/output/generator_spec_java/nesting.x/Foo.java +++ b/spec/output/generator_spec_java/nesting.x/Foo.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Foo decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.Foo); + return Objects.hash(this.Foo); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Foo other = (Foo) object; - return Objects.equal(this.Foo, other.Foo); + return Objects.equals(this.Foo, other.Foo); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Foo fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/nesting.x/MyUnion.java b/spec/output/generator_spec_java/nesting.x/MyUnion.java index 99b29134b..de9dacfea 100644 --- a/spec/output/generator_spec_java/nesting.x/MyUnion.java +++ b/spec/output/generator_spec_java/nesting.x/MyUnion.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -120,7 +120,7 @@ public static MyUnion decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.one, this.two, this.type); + return Objects.hash(this.one, this.two, this.type); } @Override public boolean equals(Object object) { @@ -129,12 +129,11 @@ public boolean equals(Object object) { } MyUnion other = (MyUnion) object; - return Objects.equal(this.one, other.one) && Objects.equal(this.two, other.two) && Objects.equal(this.type, other.type); + return Objects.equals(this.one, other.one) && Objects.equals(this.two, other.two) && Objects.equals(this.type, other.type); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -146,8 +145,7 @@ public byte[] toXdrByteArray() throws IOException { } public static MyUnion fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -179,7 +177,7 @@ public static MyUnionOne decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.someInt); + return Objects.hash(this.someInt); } @Override public boolean equals(Object object) { @@ -188,13 +186,12 @@ public boolean equals(Object object) { } MyUnionOne other = (MyUnionOne) object; - return Objects.equal(this.someInt, other.someInt); + return Objects.equals(this.someInt, other.someInt); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -206,8 +203,7 @@ public byte[] toXdrByteArray() throws IOException { } public static MyUnionOne fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -263,7 +259,7 @@ public static MyUnionTwo decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.someInt, this.foo); + return Objects.hash(this.someInt, this.foo); } @Override public boolean equals(Object object) { @@ -272,13 +268,12 @@ public boolean equals(Object object) { } MyUnionTwo other = (MyUnionTwo) object; - return Objects.equal(this.someInt, other.someInt) && Objects.equal(this.foo, other.foo); + return Objects.equals(this.someInt, other.someInt) && Objects.equals(this.foo, other.foo); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -290,8 +285,7 @@ public byte[] toXdrByteArray() throws IOException { } public static MyUnionTwo fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/nesting.x/UnionKey.java b/spec/output/generator_spec_java/nesting.x/UnionKey.java index 0ce21576b..e917d2033 100644 --- a/spec/output/generator_spec_java/nesting.x/UnionKey.java +++ b/spec/output/generator_spec_java/nesting.x/UnionKey.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -54,8 +54,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -67,8 +66,7 @@ public byte[] toXdrByteArray() throws IOException { } public static UnionKey fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/nesting.x/XdrString.java b/spec/output/generator_spec_java/nesting.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/nesting.x/XdrString.java +++ b/spec/output/generator_spec_java/nesting.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/nesting.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/nesting.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/nesting.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/nesting.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/nesting.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/nesting.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/nesting.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/nesting.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/optional.x/Arr.java b/spec/output/generator_spec_java/optional.x/Arr.java index 90661a94b..25857682e 100644 --- a/spec/output/generator_spec_java/optional.x/Arr.java +++ b/spec/output/generator_spec_java/optional.x/Arr.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -69,8 +69,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -82,8 +81,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Arr fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/optional.x/HasOptions.java b/spec/output/generator_spec_java/optional.x/HasOptions.java index 59594c86d..a4fd3924b 100644 --- a/spec/output/generator_spec_java/optional.x/HasOptions.java +++ b/spec/output/generator_spec_java/optional.x/HasOptions.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -85,7 +85,7 @@ public static HasOptions decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.firstOption, this.secondOption, this.thirdOption); + return Objects.hash(this.firstOption, this.secondOption, this.thirdOption); } @Override public boolean equals(Object object) { @@ -94,13 +94,12 @@ public boolean equals(Object object) { } HasOptions other = (HasOptions) object; - return Objects.equal(this.firstOption, other.firstOption) && Objects.equal(this.secondOption, other.secondOption) && Objects.equal(this.thirdOption, other.thirdOption); + return Objects.equals(this.firstOption, other.firstOption) && Objects.equals(this.secondOption, other.secondOption) && Objects.equals(this.thirdOption, other.thirdOption); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -112,8 +111,7 @@ public byte[] toXdrByteArray() throws IOException { } public static HasOptions fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/optional.x/XdrString.java b/spec/output/generator_spec_java/optional.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/optional.x/XdrString.java +++ b/spec/output/generator_spec_java/optional.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/optional.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/optional.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/optional.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/optional.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/optional.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/optional.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/optional.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/optional.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/struct.x/Int64.java b/spec/output/generator_spec_java/struct.x/Int64.java index 5d79c1c59..5e297453e 100644 --- a/spec/output/generator_spec_java/struct.x/Int64.java +++ b/spec/output/generator_spec_java/struct.x/Int64.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Int64 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.int64); + return Objects.hash(this.int64); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Int64 other = (Int64) object; - return Objects.equal(this.int64, other.int64); + return Objects.equals(this.int64, other.int64); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Int64 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/struct.x/MyStruct.java b/spec/output/generator_spec_java/struct.x/MyStruct.java index b9390e6fc..28d7819e6 100644 --- a/spec/output/generator_spec_java/struct.x/MyStruct.java +++ b/spec/output/generator_spec_java/struct.x/MyStruct.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; import java.util.Arrays; // === xdr source ============================================================ @@ -85,7 +85,7 @@ public static MyStruct decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.someInt, this.aBigInt, Arrays.hashCode(this.someOpaque), this.someString, this.maxString); + return Objects.hash(this.someInt, this.aBigInt, Arrays.hashCode(this.someOpaque), this.someString, this.maxString); } @Override public boolean equals(Object object) { @@ -94,13 +94,12 @@ public boolean equals(Object object) { } MyStruct other = (MyStruct) object; - return Objects.equal(this.someInt, other.someInt) && Objects.equal(this.aBigInt, other.aBigInt) && Arrays.equals(this.someOpaque, other.someOpaque) && Objects.equal(this.someString, other.someString) && Objects.equal(this.maxString, other.maxString); + return Objects.equals(this.someInt, other.someInt) && Objects.equals(this.aBigInt, other.aBigInt) && Arrays.equals(this.someOpaque, other.someOpaque) && Objects.equals(this.someString, other.someString) && Objects.equals(this.maxString, other.maxString); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -112,8 +111,7 @@ public byte[] toXdrByteArray() throws IOException { } public static MyStruct fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/struct.x/XdrString.java b/spec/output/generator_spec_java/struct.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/struct.x/XdrString.java +++ b/spec/output/generator_spec_java/struct.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/struct.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/struct.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/struct.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/struct.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/struct.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/struct.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/struct.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/struct.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/test.x/Color.java b/spec/output/generator_spec_java/test.x/Color.java index 1072a998a..263a4562d 100644 --- a/spec/output/generator_spec_java/test.x/Color.java +++ b/spec/output/generator_spec_java/test.x/Color.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -54,8 +54,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -67,8 +66,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Color fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/HasStuff.java b/spec/output/generator_spec_java/test.x/HasStuff.java index de5231a95..c7f47d1e2 100644 --- a/spec/output/generator_spec_java/test.x/HasStuff.java +++ b/spec/output/generator_spec_java/test.x/HasStuff.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -41,7 +41,7 @@ public static HasStuff decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.data); + return Objects.hash(this.data); } @Override public boolean equals(Object object) { @@ -50,13 +50,12 @@ public boolean equals(Object object) { } HasStuff other = (HasStuff) object; - return Objects.equal(this.data, other.data); + return Objects.equals(this.data, other.data); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -68,8 +67,7 @@ public byte[] toXdrByteArray() throws IOException { } public static HasStuff fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Hash.java b/spec/output/generator_spec_java/test.x/Hash.java index 7dffc3307..e3ce29bf0 100644 --- a/spec/output/generator_spec_java/test.x/Hash.java +++ b/spec/output/generator_spec_java/test.x/Hash.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -65,8 +65,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -78,8 +77,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Hash fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Hashes1.java b/spec/output/generator_spec_java/test.x/Hashes1.java index 54283c146..b5c6a8cdb 100644 --- a/spec/output/generator_spec_java/test.x/Hashes1.java +++ b/spec/output/generator_spec_java/test.x/Hashes1.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -69,8 +69,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -82,8 +81,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Hashes1 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Hashes2.java b/spec/output/generator_spec_java/test.x/Hashes2.java index 3d54d807d..0af1a9e15 100644 --- a/spec/output/generator_spec_java/test.x/Hashes2.java +++ b/spec/output/generator_spec_java/test.x/Hashes2.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -70,8 +70,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -83,8 +82,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Hashes2 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Hashes3.java b/spec/output/generator_spec_java/test.x/Hashes3.java index f204a03bd..5f428897d 100644 --- a/spec/output/generator_spec_java/test.x/Hashes3.java +++ b/spec/output/generator_spec_java/test.x/Hashes3.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -70,8 +70,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -83,8 +82,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Hashes3 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Int1.java b/spec/output/generator_spec_java/test.x/Int1.java index 17fe01b58..079ddf12f 100644 --- a/spec/output/generator_spec_java/test.x/Int1.java +++ b/spec/output/generator_spec_java/test.x/Int1.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Int1 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.int1); + return Objects.hash(this.int1); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Int1 other = (Int1) object; - return Objects.equal(this.int1, other.int1); + return Objects.equals(this.int1, other.int1); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Int1 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Int2.java b/spec/output/generator_spec_java/test.x/Int2.java index 67fc0f9e1..3f9bc5926 100644 --- a/spec/output/generator_spec_java/test.x/Int2.java +++ b/spec/output/generator_spec_java/test.x/Int2.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Int2 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.int2); + return Objects.hash(this.int2); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Int2 other = (Int2) object; - return Objects.equal(this.int2, other.int2); + return Objects.equals(this.int2, other.int2); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Int2 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Int3.java b/spec/output/generator_spec_java/test.x/Int3.java index c80fc74dd..3a21ac493 100644 --- a/spec/output/generator_spec_java/test.x/Int3.java +++ b/spec/output/generator_spec_java/test.x/Int3.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Int3 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.int3); + return Objects.hash(this.int3); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Int3 other = (Int3) object; - return Objects.equal(this.int3, other.int3); + return Objects.equals(this.int3, other.int3); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Int3 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Int4.java b/spec/output/generator_spec_java/test.x/Int4.java index f90f395f7..1f50aa110 100644 --- a/spec/output/generator_spec_java/test.x/Int4.java +++ b/spec/output/generator_spec_java/test.x/Int4.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Int4 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.int4); + return Objects.hash(this.int4); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Int4 other = (Int4) object; - return Objects.equal(this.int4, other.int4); + return Objects.equals(this.int4, other.int4); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Int4 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/LotsOfMyStructs.java b/spec/output/generator_spec_java/test.x/LotsOfMyStructs.java index 936144fb0..84ef5f7ab 100644 --- a/spec/output/generator_spec_java/test.x/LotsOfMyStructs.java +++ b/spec/output/generator_spec_java/test.x/LotsOfMyStructs.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -63,8 +63,7 @@ public boolean equals(Object object) { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -76,8 +75,7 @@ public byte[] toXdrByteArray() throws IOException { } public static LotsOfMyStructs fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/MyStruct.java b/spec/output/generator_spec_java/test.x/MyStruct.java index 2b07ecbf4..3cdc263a1 100644 --- a/spec/output/generator_spec_java/test.x/MyStruct.java +++ b/spec/output/generator_spec_java/test.x/MyStruct.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -101,7 +101,7 @@ public static MyStruct decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.field1, this.field2, this.field3, this.field4, this.field5, this.field6, this.field7); + return Objects.hash(this.field1, this.field2, this.field3, this.field4, this.field5, this.field6, this.field7); } @Override public boolean equals(Object object) { @@ -110,13 +110,12 @@ public boolean equals(Object object) { } MyStruct other = (MyStruct) object; - return Objects.equal(this.field1, other.field1) && Objects.equal(this.field2, other.field2) && Objects.equal(this.field3, other.field3) && Objects.equal(this.field4, other.field4) && Objects.equal(this.field5, other.field5) && Objects.equal(this.field6, other.field6) && Objects.equal(this.field7, other.field7); + return Objects.equals(this.field1, other.field1) && Objects.equals(this.field2, other.field2) && Objects.equals(this.field3, other.field3) && Objects.equals(this.field4, other.field4) && Objects.equals(this.field5, other.field5) && Objects.equals(this.field6, other.field6) && Objects.equals(this.field7, other.field7); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -128,8 +127,7 @@ public byte[] toXdrByteArray() throws IOException { } public static MyStruct fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Nester.java b/spec/output/generator_spec_java/test.x/Nester.java index d3d1bd086..787e690af 100644 --- a/spec/output/generator_spec_java/test.x/Nester.java +++ b/spec/output/generator_spec_java/test.x/Nester.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -75,7 +75,7 @@ public static Nester decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.nestedEnum, this.nestedStruct, this.nestedUnion); + return Objects.hash(this.nestedEnum, this.nestedStruct, this.nestedUnion); } @Override public boolean equals(Object object) { @@ -84,13 +84,12 @@ public boolean equals(Object object) { } Nester other = (Nester) object; - return Objects.equal(this.nestedEnum, other.nestedEnum) && Objects.equal(this.nestedStruct, other.nestedStruct) && Objects.equal(this.nestedUnion, other.nestedUnion); + return Objects.equals(this.nestedEnum, other.nestedEnum) && Objects.equals(this.nestedStruct, other.nestedStruct) && Objects.equals(this.nestedUnion, other.nestedUnion); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -102,8 +101,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Nester fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -174,8 +172,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -187,8 +184,7 @@ public byte[] toXdrByteArray() throws IOException { } public static NestedEnum fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -221,7 +217,7 @@ public static NesterNestedStruct decode(XdrDataInputStream stream) throws IOExce } @Override public int hashCode() { - return Objects.hashCode(this.blah); + return Objects.hash(this.blah); } @Override public boolean equals(Object object) { @@ -230,13 +226,12 @@ public boolean equals(Object object) { } NesterNestedStruct other = (NesterNestedStruct) object; - return Objects.equal(this.blah, other.blah); + return Objects.equals(this.blah, other.blah); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -248,8 +243,7 @@ public byte[] toXdrByteArray() throws IOException { } public static NesterNestedStruct fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -343,7 +337,7 @@ public static NesterNestedUnion decode(XdrDataInputStream stream) throws IOExcep } @Override public int hashCode() { - return Objects.hashCode(this.blah2, this.color); + return Objects.hash(this.blah2, this.color); } @Override public boolean equals(Object object) { @@ -352,12 +346,11 @@ public boolean equals(Object object) { } NesterNestedUnion other = (NesterNestedUnion) object; - return Objects.equal(this.blah2, other.blah2) && Objects.equal(this.color, other.color); + return Objects.equals(this.blah2, other.blah2) && Objects.equals(this.color, other.color); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -369,8 +362,7 @@ public byte[] toXdrByteArray() throws IOException { } public static NesterNestedUnion fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/OptHash1.java b/spec/output/generator_spec_java/test.x/OptHash1.java index 18b6bb54e..0a64e8058 100644 --- a/spec/output/generator_spec_java/test.x/OptHash1.java +++ b/spec/output/generator_spec_java/test.x/OptHash1.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -56,7 +56,7 @@ public static OptHash1 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.optHash1); + return Objects.hash(this.optHash1); } @Override @@ -66,12 +66,11 @@ public boolean equals(Object object) { } OptHash1 other = (OptHash1) object; - return Objects.equal(this.optHash1, other.optHash1); + return Objects.equals(this.optHash1, other.optHash1); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -83,8 +82,7 @@ public byte[] toXdrByteArray() throws IOException { } public static OptHash1 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/OptHash2.java b/spec/output/generator_spec_java/test.x/OptHash2.java index 56207e366..bb087923c 100644 --- a/spec/output/generator_spec_java/test.x/OptHash2.java +++ b/spec/output/generator_spec_java/test.x/OptHash2.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -56,7 +56,7 @@ public static OptHash2 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.optHash2); + return Objects.hash(this.optHash2); } @Override @@ -66,12 +66,11 @@ public boolean equals(Object object) { } OptHash2 other = (OptHash2) object; - return Objects.equal(this.optHash2, other.optHash2); + return Objects.equals(this.optHash2, other.optHash2); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -83,8 +82,7 @@ public byte[] toXdrByteArray() throws IOException { } public static OptHash2 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Str.java b/spec/output/generator_spec_java/test.x/Str.java index ea3343b97..e222e4106 100644 --- a/spec/output/generator_spec_java/test.x/Str.java +++ b/spec/output/generator_spec_java/test.x/Str.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Str decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.str); + return Objects.hash(this.str); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Str other = (Str) object; - return Objects.equal(this.str, other.str); + return Objects.equals(this.str, other.str); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Str fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Str2.java b/spec/output/generator_spec_java/test.x/Str2.java index 3d30b06d2..4b6cdd709 100644 --- a/spec/output/generator_spec_java/test.x/Str2.java +++ b/spec/output/generator_spec_java/test.x/Str2.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Str2 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.str2); + return Objects.hash(this.str2); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Str2 other = (Str2) object; - return Objects.equal(this.str2, other.str2); + return Objects.equals(this.str2, other.str2); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Str2 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Uint512.java b/spec/output/generator_spec_java/test.x/Uint512.java index e2301e572..a091d1ec8 100644 --- a/spec/output/generator_spec_java/test.x/Uint512.java +++ b/spec/output/generator_spec_java/test.x/Uint512.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -65,8 +65,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -78,8 +77,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Uint512 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Uint513.java b/spec/output/generator_spec_java/test.x/Uint513.java index f96d25cea..06bb768af 100644 --- a/spec/output/generator_spec_java/test.x/Uint513.java +++ b/spec/output/generator_spec_java/test.x/Uint513.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -66,8 +66,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -79,8 +78,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Uint513 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/Uint514.java b/spec/output/generator_spec_java/test.x/Uint514.java index 11e329996..719b430fb 100644 --- a/spec/output/generator_spec_java/test.x/Uint514.java +++ b/spec/output/generator_spec_java/test.x/Uint514.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -66,8 +66,7 @@ public boolean equals(Object object) { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -79,8 +78,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Uint514 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/test.x/XdrString.java b/spec/output/generator_spec_java/test.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/test.x/XdrString.java +++ b/spec/output/generator_spec_java/test.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/test.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/test.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/test.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/test.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/test.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/test.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/test.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/test.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/union.x/Error.java b/spec/output/generator_spec_java/union.x/Error.java index 2d88902e1..c5b1bc493 100644 --- a/spec/output/generator_spec_java/union.x/Error.java +++ b/spec/output/generator_spec_java/union.x/Error.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Error decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.Error); + return Objects.hash(this.Error); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Error other = (Error) object; - return Objects.equal(this.Error, other.Error); + return Objects.equals(this.Error, other.Error); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Error fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/union.x/IntUnion.java b/spec/output/generator_spec_java/union.x/IntUnion.java index ab0e48dd9..71c82c7d0 100644 --- a/spec/output/generator_spec_java/union.x/IntUnion.java +++ b/spec/output/generator_spec_java/union.x/IntUnion.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; import java.util.Arrays; // === xdr source ============================================================ @@ -117,7 +117,7 @@ public static IntUnion decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.error, Arrays.hashCode(this.things), this.type); + return Objects.hash(this.error, Arrays.hashCode(this.things), this.type); } @Override public boolean equals(Object object) { @@ -126,12 +126,11 @@ public boolean equals(Object object) { } IntUnion other = (IntUnion) object; - return Objects.equal(this.error, other.error) && Arrays.equals(this.things, other.things) && Objects.equal(this.type, other.type); + return Objects.equals(this.error, other.error) && Arrays.equals(this.things, other.things) && Objects.equals(this.type, other.type); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -143,8 +142,7 @@ public byte[] toXdrByteArray() throws IOException { } public static IntUnion fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/union.x/IntUnion2.java b/spec/output/generator_spec_java/union.x/IntUnion2.java index 5a5558da1..19c95ed8c 100644 --- a/spec/output/generator_spec_java/union.x/IntUnion2.java +++ b/spec/output/generator_spec_java/union.x/IntUnion2.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static IntUnion2 decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.IntUnion2); + return Objects.hash(this.IntUnion2); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } IntUnion2 other = (IntUnion2) object; - return Objects.equal(this.IntUnion2, other.IntUnion2); + return Objects.equals(this.IntUnion2, other.IntUnion2); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static IntUnion2 fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/union.x/Multi.java b/spec/output/generator_spec_java/union.x/Multi.java index f910ad807..c3ac21f08 100644 --- a/spec/output/generator_spec_java/union.x/Multi.java +++ b/spec/output/generator_spec_java/union.x/Multi.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; // === xdr source ============================================================ @@ -48,7 +48,7 @@ public static Multi decode(XdrDataInputStream stream) throws IOException { @Override public int hashCode() { - return Objects.hashCode(this.Multi); + return Objects.hash(this.Multi); } @Override @@ -58,12 +58,11 @@ public boolean equals(Object object) { } Multi other = (Multi) object; - return Objects.equal(this.Multi, other.Multi); + return Objects.equals(this.Multi, other.Multi); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +74,7 @@ public byte[] toXdrByteArray() throws IOException { } public static Multi fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/union.x/MyUnion.java b/spec/output/generator_spec_java/union.x/MyUnion.java index 0798460a6..3a5bea607 100644 --- a/spec/output/generator_spec_java/union.x/MyUnion.java +++ b/spec/output/generator_spec_java/union.x/MyUnion.java @@ -6,10 +6,10 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import com.google.common.base.Objects; +import java.util.Objects; import java.util.Arrays; // === xdr source ============================================================ @@ -118,7 +118,7 @@ public static MyUnion decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.error, Arrays.hashCode(this.things), this.type); + return Objects.hash(this.error, Arrays.hashCode(this.things), this.type); } @Override public boolean equals(Object object) { @@ -127,12 +127,11 @@ public boolean equals(Object object) { } MyUnion other = (MyUnion) object; - return Objects.equal(this.error, other.error) && Arrays.equals(this.things, other.things) && Objects.equal(this.type, other.type); + return Objects.equals(this.error, other.error) && Arrays.equals(this.things, other.things) && Objects.equals(this.type, other.type); } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -144,8 +143,7 @@ public byte[] toXdrByteArray() throws IOException { } public static MyUnion fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/union.x/UnionKey.java b/spec/output/generator_spec_java/union.x/UnionKey.java index 2cd199024..07ffbc088 100644 --- a/spec/output/generator_spec_java/union.x/UnionKey.java +++ b/spec/output/generator_spec_java/union.x/UnionKey.java @@ -6,7 +6,7 @@ import java.io.IOException; import static MyXDR.Constants.*; -import com.google.common.io.BaseEncoding; +import org.stellar.sdk.Base64Factory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -51,8 +51,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -64,8 +63,7 @@ public byte[] toXdrByteArray() throws IOException { } public static UnionKey fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } diff --git a/spec/output/generator_spec_java/union.x/XdrString.java b/spec/output/generator_spec_java/union.x/XdrString.java index 8f9b2ab73..bb4e81fb4 100644 --- a/spec/output/generator_spec_java/union.x/XdrString.java +++ b/spec/output/generator_spec_java/union.x/XdrString.java @@ -1,12 +1,12 @@ package MyXDR; -import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; +import org.stellar.sdk.Base64Factory; public class XdrString implements XdrElement { private byte[] bytes; @@ -41,8 +41,7 @@ public byte[] getBytes() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -54,8 +53,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); } diff --git a/spec/output/generator_spec_java/union.x/XdrUnsignedHyperInteger.java b/spec/output/generator_spec_java/union.x/XdrUnsignedHyperInteger.java index 6789d46c1..6c05ca57d 100644 --- a/spec/output/generator_spec_java/union.x/XdrUnsignedHyperInteger.java +++ b/spec/output/generator_spec_java/union.x/XdrUnsignedHyperInteger.java @@ -1,12 +1,11 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Hyper Integer. @@ -62,8 +61,7 @@ public BigInteger getNumber() { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -75,8 +73,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedHyperInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -88,7 +85,7 @@ public static XdrUnsignedHyperInteger fromXdrByteArray(byte[] xdr) throws IOExce @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -98,7 +95,7 @@ public boolean equals(Object object) { } XdrUnsignedHyperInteger other = (XdrUnsignedHyperInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_java/union.x/XdrUnsignedInteger.java b/spec/output/generator_spec_java/union.x/XdrUnsignedInteger.java index a8967063c..8f2a52395 100644 --- a/spec/output/generator_spec_java/union.x/XdrUnsignedInteger.java +++ b/spec/output/generator_spec_java/union.x/XdrUnsignedInteger.java @@ -1,11 +1,10 @@ -package MyXDR; - -import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; +package org.stellar.sdk.xdr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Objects; +import org.stellar.sdk.Base64Factory; /** * Represents XDR Unsigned Integer. @@ -50,8 +49,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { @Override public String toXdrBase64() throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(toXdrByteArray()); + return Base64Factory.getInstance().encodeToString(toXdrByteArray()); } @Override @@ -63,8 +61,7 @@ public byte[] toXdrByteArray() throws IOException { } public static XdrUnsignedInteger fromXdrBase64(String xdr) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(xdr); + byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); } @@ -76,7 +73,7 @@ public static XdrUnsignedInteger fromXdrByteArray(byte[] xdr) throws IOException @Override public int hashCode() { - return Objects.hashCode(this.number); + return Objects.hash(this.number); } @Override @@ -86,7 +83,7 @@ public boolean equals(Object object) { } XdrUnsignedInteger other = (XdrUnsignedInteger) object; - return Objects.equal(this.number, other.number); + return Objects.equals(this.number, other.number); } public String toString() { diff --git a/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs b/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs index ea58ac338..cbd950c3d 100644 --- a/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - #[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2358,7 +2771,7 @@ impl From for i32 { impl ReadXdr for AccountFlags { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -2369,7 +2782,7 @@ impl ReadXdr for AccountFlags { impl WriteXdr for AccountFlags { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -2447,21 +2860,21 @@ impl Type { #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::AccountFlags => r.with_limited_depth(|r| Ok(Self::AccountFlags(Box::new(AccountFlags::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2473,52 +2886,60 @@ impl Type { } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::AccountFlags(Box::new(t.0))))), + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::AccountFlags(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::AccountFlags => Ok(Self::AccountFlags(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -2564,3 +2985,13 @@ impl Variants for Type { Self::VARIANTS.iter() } } + +impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::AccountFlags(v) => v.write_xdr(w), + } + } +} diff --git a/spec/output/generator_spec_rust/const.x/MyXDR.rs b/spec/output/generator_spec_rust/const.x/MyXDR.rs index c584b1859..18b47560b 100644 --- a/spec/output/generator_spec_rust/const.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/const.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - #[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2378,7 +2791,7 @@ TypeVariant::TestArray2, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::TestArray => r.with_limited_depth(|r| Ok(Self::TestArray(Box::new(TestArray::read_xdr(r)?)))), TypeVariant::TestArray2 => r.with_limited_depth(|r| Ok(Self::TestArray2(Box::new(TestArray2::read_xdr(r)?)))), @@ -2386,14 +2799,14 @@ TypeVariant::TestArray2 => r.with_limited_depth(|r| Ok(Self::TestArray2(Box::new } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2405,55 +2818,64 @@ TypeVariant::TestArray2 => r.with_limited_depth(|r| Ok(Self::TestArray2(Box::new } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), -TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::TestArray => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray(Box::new(t.0))))), -TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray2(Box::new(t.0))))), + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray(Box::new(t.0))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray2(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), -TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::TestArray => Ok(Self::TestArray(Box::new(serde_json::from_reader(r)?))), +TypeVariant::TestArray2 => Ok(Self::TestArray2(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -2502,3 +2924,14 @@ Self::TestArray2(_) => TypeVariant::TestArray2, Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::TestArray(v) => v.write_xdr(w), +Self::TestArray2(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust/enum.x/MyXDR.rs b/spec/output/generator_spec_rust/enum.x/MyXDR.rs index 1b7ac2cc3..5066acafb 100644 --- a/spec/output/generator_spec_rust/enum.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/enum.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - #[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2442,7 +2855,7 @@ Self::FbaMessage => "FbaMessage", impl ReadXdr for MessageType { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -2453,7 +2866,7 @@ Self::FbaMessage => "FbaMessage", impl WriteXdr for MessageType { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -2548,7 +2961,7 @@ Self::Blue => "Blue", impl ReadXdr for Color { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -2559,7 +2972,7 @@ Self::Blue => "Blue", impl WriteXdr for Color { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -2654,7 +3067,7 @@ Self::Blue2 => "Blue2", impl ReadXdr for Color2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -2665,7 +3078,7 @@ Self::Blue2 => "Blue2", impl WriteXdr for Color2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -2759,7 +3172,7 @@ TypeVariant::Color2, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::MessageType => r.with_limited_depth(|r| Ok(Self::MessageType(Box::new(MessageType::read_xdr(r)?)))), TypeVariant::Color => r.with_limited_depth(|r| Ok(Self::Color(Box::new(Color::read_xdr(r)?)))), @@ -2768,14 +3181,14 @@ TypeVariant::Color2 => r.with_limited_depth(|r| Ok(Self::Color2(Box::new(Color2: } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2787,58 +3200,68 @@ TypeVariant::Color2 => r.with_limited_depth(|r| Ok(Self::Color2(Box::new(Color2: } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color2(Box::new(t))))), + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color2(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::MessageType => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MessageType(Box::new(t.0))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), -TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color2(Box::new(t.0))))), + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MessageType(Box::new(t.0))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color2(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Color2(Box::new(t))))), + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Color2(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::MessageType => Ok(Self::MessageType(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Color => Ok(Self::Color(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Color2 => Ok(Self::Color2(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -2890,3 +3313,15 @@ Self::Color2(_) => TypeVariant::Color2, Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::MessageType(v) => v.write_xdr(w), +Self::Color(v) => v.write_xdr(w), +Self::Color2(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust/nesting.x/MyXDR.rs b/spec/output/generator_spec_rust/nesting.x/MyXDR.rs index a0b646d15..a71bb0182 100644 --- a/spec/output/generator_spec_rust/nesting.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/nesting.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - #[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2369,7 +2782,7 @@ Self::Offer => "Offer", impl ReadXdr for UnionKey { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -2380,7 +2793,7 @@ Self::Offer => "Offer", impl WriteXdr for UnionKey { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -2409,7 +2822,7 @@ pub struct MyUnionOne { impl ReadXdr for MyUnionOne { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ some_int: i32::read_xdr(r)?, @@ -2420,7 +2833,7 @@ impl ReadXdr for MyUnionOne { impl WriteXdr for MyUnionOne { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.some_int.write_xdr(w)?; Ok(()) @@ -2445,7 +2858,7 @@ pub struct MyUnionTwo { impl ReadXdr for MyUnionTwo { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ some_int: i32::read_xdr(r)?, @@ -2457,7 +2870,7 @@ foo: i32::read_xdr(r)?, impl WriteXdr for MyUnionTwo { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.some_int.write_xdr(w)?; self.foo.write_xdr(w)?; @@ -2557,7 +2970,7 @@ Self::Offer => UnionKey::Offer, impl ReadXdr for MyUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let dv: UnionKey = ::read_xdr(r)?; #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] @@ -2575,7 +2988,7 @@ UnionKey::Offer => Self::Offer, impl WriteXdr for MyUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.discriminant().write_xdr(w)?; #[allow(clippy::match_same_arms)] @@ -2691,7 +3104,7 @@ TypeVariant::MyUnionTwo, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::UnionKey => r.with_limited_depth(|r| Ok(Self::UnionKey(Box::new(UnionKey::read_xdr(r)?)))), TypeVariant::Foo => r.with_limited_depth(|r| Ok(Self::Foo(Box::new(Foo::read_xdr(r)?)))), @@ -2702,14 +3115,14 @@ TypeVariant::MyUnionTwo => r.with_limited_depth(|r| Ok(Self::MyUnionTwo(Box::new } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2721,64 +3134,76 @@ TypeVariant::MyUnionTwo => r.with_limited_depth(|r| Ok(Self::MyUnionTwo(Box::new } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Foo(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), -TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Foo(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), -TypeVariant::Foo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Foo(Box::new(t.0))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), -TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t.0))))), -TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t.0))))), + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Foo(Box::new(t.0))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t.0))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Foo(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), -TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Foo(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::UnionKey => Ok(Self::UnionKey(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Foo => Ok(Self::Foo(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnion => Ok(Self::MyUnion(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnionOne => Ok(Self::MyUnionOne(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnionTwo => Ok(Self::MyUnionTwo(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -2836,3 +3261,17 @@ Self::MyUnionTwo(_) => TypeVariant::MyUnionTwo, Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::UnionKey(v) => v.write_xdr(w), +Self::Foo(v) => v.write_xdr(w), +Self::MyUnion(v) => v.write_xdr(w), +Self::MyUnionOne(v) => v.write_xdr(w), +Self::MyUnionTwo(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust/optional.x/MyXDR.rs b/spec/output/generator_spec_rust/optional.x/MyXDR.rs index 205b82a06..c54e9bc24 100644 --- a/spec/output/generator_spec_rust/optional.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/optional.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - #[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2308,7 +2721,7 @@ pub struct HasOptions { impl ReadXdr for HasOptions { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ first_option: Option::::read_xdr(r)?, @@ -2321,7 +2734,7 @@ third_option: Option::::read_xdr(r)?, impl WriteXdr for HasOptions { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.first_option.write_xdr(w)?; self.second_option.write_xdr(w)?; @@ -2409,7 +2822,7 @@ TypeVariant::HasOptions, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::Arr => r.with_limited_depth(|r| Ok(Self::Arr(Box::new(Arr::read_xdr(r)?)))), TypeVariant::HasOptions => r.with_limited_depth(|r| Ok(Self::HasOptions(Box::new(HasOptions::read_xdr(r)?)))), @@ -2417,14 +2830,14 @@ TypeVariant::HasOptions => r.with_limited_depth(|r| Ok(Self::HasOptions(Box::new } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2436,55 +2849,64 @@ TypeVariant::HasOptions => r.with_limited_depth(|r| Ok(Self::HasOptions(Box::new } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Arr(Box::new(t))))), -TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Arr(Box::new(t))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::Arr => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Arr(Box::new(t.0))))), -TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasOptions(Box::new(t.0))))), + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Arr(Box::new(t.0))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasOptions(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Arr(Box::new(t))))), -TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Arr(Box::new(t))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::Arr => Ok(Self::Arr(Box::new(serde_json::from_reader(r)?))), +TypeVariant::HasOptions => Ok(Self::HasOptions(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -2533,3 +2955,14 @@ Self::HasOptions(_) => TypeVariant::HasOptions, Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::Arr(v) => v.write_xdr(w), +Self::HasOptions(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust/struct.x/MyXDR.rs b/spec/output/generator_spec_rust/struct.x/MyXDR.rs index 323af1f94..d36ed07c5 100644 --- a/spec/output/generator_spec_rust/struct.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/struct.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - #[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2312,7 +2725,7 @@ pub struct MyStruct { impl ReadXdr for MyStruct { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ some_int: i32::read_xdr(r)?, @@ -2327,7 +2740,7 @@ max_string: StringM::<100>::read_xdr(r)?, impl WriteXdr for MyStruct { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.some_int.write_xdr(w)?; self.a_big_int.write_xdr(w)?; @@ -2417,7 +2830,7 @@ TypeVariant::MyStruct, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::Int64 => r.with_limited_depth(|r| Ok(Self::Int64(Box::new(Int64::read_xdr(r)?)))), TypeVariant::MyStruct => r.with_limited_depth(|r| Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?)))), @@ -2425,14 +2838,14 @@ TypeVariant::MyStruct => r.with_limited_depth(|r| Ok(Self::MyStruct(Box::new(MyS } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2444,55 +2857,64 @@ TypeVariant::MyStruct => r.with_limited_depth(|r| Ok(Self::MyStruct(Box::new(MyS } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int64(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int64(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int64(Box::new(t.0))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int64(Box::new(t.0))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int64(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int64(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::Int64 => Ok(Self::Int64(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyStruct => Ok(Self::MyStruct(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -2541,3 +2963,14 @@ Self::MyStruct(_) => TypeVariant::MyStruct, Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::Int64(v) => v.write_xdr(w), +Self::MyStruct(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust/test.x/MyXDR.rs b/spec/output/generator_spec_rust/test.x/MyXDR.rs index 9ba9db5a2..d1f590d66 100644 --- a/spec/output/generator_spec_rust/test.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/test.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. #[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - -#[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2291,24 +2704,23 @@ mod test { #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] pub struct Uint512(pub [u8; 64]); -impl core::fmt::Display for Uint512 { +impl core::fmt::Debug for Uint512 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let v = &self.0; + write!(f, "Uint512(")?; for b in v { write!(f, "{b:02x}")?; } + write!(f, ")")?; Ok(()) } } - -impl core::fmt::Debug for Uint512 { +impl core::fmt::Display for Uint512 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let v = &self.0; - write!(f, "Uint512(")?; for b in v { write!(f, "{b:02x}")?; } - write!(f, ")")?; Ok(()) } } @@ -2343,7 +2755,7 @@ impl AsRef<[u8; 64]> for Uint512 { impl ReadXdr for Uint512 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = <[u8; 64]>::read_xdr(r)?; let v = Uint512(i); @@ -2354,7 +2766,7 @@ impl ReadXdr for Uint512 { impl WriteXdr for Uint512 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2403,8 +2815,8 @@ impl AsRef<[u8]> for Uint512 { #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[derive(Default)] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct Uint513(pub BytesM::<64>); impl From for BytesM::<64> { @@ -2430,7 +2842,7 @@ impl AsRef> for Uint513 { impl ReadXdr for Uint513 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = BytesM::<64>::read_xdr(r)?; let v = Uint513(i); @@ -2441,7 +2853,7 @@ impl ReadXdr for Uint513 { impl WriteXdr for Uint513 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2502,8 +2914,8 @@ impl AsRef<[u8]> for Uint513 { #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[derive(Default)] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct Uint514(pub BytesM); impl From for BytesM { @@ -2529,7 +2941,7 @@ impl AsRef for Uint514 { impl ReadXdr for Uint514 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = BytesM::read_xdr(r)?; let v = Uint514(i); @@ -2540,7 +2952,7 @@ impl ReadXdr for Uint514 { impl WriteXdr for Uint514 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2601,8 +3013,8 @@ impl AsRef<[u8]> for Uint514 { #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[derive(Default)] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct Str(pub StringM::<64>); impl From for StringM::<64> { @@ -2628,7 +3040,7 @@ impl AsRef> for Str { impl ReadXdr for Str { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = StringM::<64>::read_xdr(r)?; let v = Str(i); @@ -2639,7 +3051,7 @@ impl ReadXdr for Str { impl WriteXdr for Str { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2700,8 +3112,8 @@ impl AsRef<[u8]> for Str { #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[derive(Default)] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct Str2(pub StringM); impl From for StringM { @@ -2727,7 +3139,7 @@ impl AsRef for Str2 { impl ReadXdr for Str2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = StringM::read_xdr(r)?; let v = Str2(i); @@ -2738,7 +3150,7 @@ impl ReadXdr for Str2 { impl WriteXdr for Str2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2801,24 +3213,23 @@ impl AsRef<[u8]> for Str2 { #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] pub struct Hash(pub [u8; 32]); -impl core::fmt::Display for Hash { +impl core::fmt::Debug for Hash { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let v = &self.0; + write!(f, "Hash(")?; for b in v { write!(f, "{b:02x}")?; } + write!(f, ")")?; Ok(()) } } - -impl core::fmt::Debug for Hash { +impl core::fmt::Display for Hash { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let v = &self.0; - write!(f, "Hash(")?; for b in v { write!(f, "{b:02x}")?; } - write!(f, ")")?; Ok(()) } } @@ -2853,7 +3264,7 @@ impl AsRef<[u8; 32]> for Hash { impl ReadXdr for Hash { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = <[u8; 32]>::read_xdr(r)?; let v = Hash(i); @@ -2864,7 +3275,7 @@ impl ReadXdr for Hash { impl WriteXdr for Hash { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2912,8 +3323,8 @@ impl AsRef<[u8]> for Hash { // #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct Hashes1(pub [Hash; 12]); impl From for [Hash; 12] { @@ -2939,7 +3350,7 @@ impl AsRef<[Hash; 12]> for Hashes1 { impl ReadXdr for Hashes1 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = <[Hash; 12]>::read_xdr(r)?; let v = Hashes1(i); @@ -2950,7 +3361,7 @@ impl ReadXdr for Hashes1 { impl WriteXdr for Hashes1 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2999,8 +3410,8 @@ impl AsRef<[Hash]> for Hashes1 { #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[derive(Default)] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct Hashes2(pub VecM::); impl From for VecM:: { @@ -3026,7 +3437,7 @@ impl AsRef> for Hashes2 { impl ReadXdr for Hashes2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = VecM::::read_xdr(r)?; let v = Hashes2(i); @@ -3037,7 +3448,7 @@ impl ReadXdr for Hashes2 { impl WriteXdr for Hashes2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -3098,8 +3509,8 @@ impl AsRef<[Hash]> for Hashes2 { #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[derive(Default)] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct Hashes3(pub VecM::); impl From for VecM:: { @@ -3125,7 +3536,7 @@ impl AsRef> for Hashes3 { impl ReadXdr for Hashes3 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = VecM::::read_xdr(r)?; let v = Hashes3(i); @@ -3136,7 +3547,7 @@ impl ReadXdr for Hashes3 { impl WriteXdr for Hashes3 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -3196,8 +3607,8 @@ impl AsRef<[Hash]> for Hashes3 { // #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct OptHash1(pub Option); impl From for Option { @@ -3223,7 +3634,7 @@ impl AsRef> for OptHash1 { impl ReadXdr for OptHash1 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = Option::::read_xdr(r)?; let v = OptHash1(i); @@ -3234,7 +3645,7 @@ impl ReadXdr for OptHash1 { impl WriteXdr for OptHash1 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -3245,8 +3656,8 @@ impl WriteXdr for OptHash1 { // #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct OptHash2(pub Option); impl From for Option { @@ -3272,7 +3683,7 @@ impl AsRef> for OptHash2 { impl ReadXdr for OptHash2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = Option::::read_xdr(r)?; let v = OptHash2(i); @@ -3283,7 +3694,7 @@ impl ReadXdr for OptHash2 { impl WriteXdr for OptHash2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -3340,7 +3751,7 @@ pub struct MyStruct { impl ReadXdr for MyStruct { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ field1: Uint512::read_xdr(r)?, @@ -3357,7 +3768,7 @@ field7: bool::read_xdr(r)?, impl WriteXdr for MyStruct { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.field1.write_xdr(w)?; self.field2.write_xdr(w)?; @@ -3387,7 +3798,7 @@ pub struct LotsOfMyStructs { impl ReadXdr for LotsOfMyStructs { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ members: VecM::::read_xdr(r)?, @@ -3398,7 +3809,7 @@ impl ReadXdr for LotsOfMyStructs { impl WriteXdr for LotsOfMyStructs { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.members.write_xdr(w)?; Ok(()) @@ -3422,7 +3833,7 @@ pub struct HasStuff { impl ReadXdr for HasStuff { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ data: LotsOfMyStructs::read_xdr(r)?, @@ -3433,7 +3844,7 @@ impl ReadXdr for HasStuff { impl WriteXdr for HasStuff { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.data.write_xdr(w)?; Ok(()) @@ -3528,7 +3939,7 @@ Self::Green => "Green", impl ReadXdr for Color { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -3539,7 +3950,7 @@ Self::Green => "Green", impl WriteXdr for Color { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -3640,7 +4051,7 @@ Self::2 => "2", impl ReadXdr for NesterNestedEnum { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -3651,7 +4062,7 @@ Self::2 => "2", impl WriteXdr for NesterNestedEnum { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -3674,7 +4085,7 @@ pub struct NesterNestedStruct { impl ReadXdr for NesterNestedStruct { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ blah: i32::read_xdr(r)?, @@ -3685,7 +4096,7 @@ impl ReadXdr for NesterNestedStruct { impl WriteXdr for NesterNestedStruct { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.blah.write_xdr(w)?; Ok(()) @@ -3764,7 +4175,7 @@ impl Union for NesterNestedUnion {} impl ReadXdr for NesterNestedUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let dv: Color = ::read_xdr(r)?; #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] @@ -3780,7 +4191,7 @@ impl ReadXdr for NesterNestedUnion { impl WriteXdr for NesterNestedUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.discriminant().write_xdr(w)?; #[allow(clippy::match_same_arms)] @@ -3826,7 +4237,7 @@ pub struct Nester { impl ReadXdr for Nester { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { Ok(Self{ nested_enum: NesterNestedEnum::read_xdr(r)?, @@ -3839,7 +4250,7 @@ nested_union: NesterNestedUnion::read_xdr(r)?, impl WriteXdr for Nester { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.nested_enum.write_xdr(w)?; self.nested_struct.write_xdr(w)?; @@ -4095,7 +4506,7 @@ TypeVariant::NesterNestedUnion, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::Uint512 => r.with_limited_depth(|r| Ok(Self::Uint512(Box::new(Uint512::read_xdr(r)?)))), TypeVariant::Uint513 => r.with_limited_depth(|r| Ok(Self::Uint513(Box::new(Uint513::read_xdr(r)?)))), @@ -4124,14 +4535,14 @@ TypeVariant::NesterNestedUnion => r.with_limited_depth(|r| Ok(Self::NesterNested } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -4143,118 +4554,148 @@ TypeVariant::NesterNestedUnion => r.with_limited_depth(|r| Ok(Self::NesterNested } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), -TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), -TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), -TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str(Box::new(t))))), -TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str2(Box::new(t))))), -TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hash(Box::new(t))))), -TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), -TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), -TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), -TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), -TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), -TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int1(Box::new(t))))), -TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int2(Box::new(t))))), -TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int3(Box::new(t))))), -TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int4(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), -TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), -TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Nester(Box::new(t))))), -TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), -TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), -TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str(Box::new(t))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str2(Box::new(t))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hash(Box::new(t))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int1(Box::new(t))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int2(Box::new(t))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int3(Box::new(t))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int4(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Nester(Box::new(t))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint512(Box::new(t.0))))), -TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint513(Box::new(t.0))))), -TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint514(Box::new(t.0))))), -TypeVariant::Str => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str(Box::new(t.0))))), -TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str2(Box::new(t.0))))), -TypeVariant::Hash => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hash(Box::new(t.0))))), -TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes1(Box::new(t.0))))), -TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes2(Box::new(t.0))))), -TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes3(Box::new(t.0))))), -TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash1(Box::new(t.0))))), -TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash2(Box::new(t.0))))), -TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int1(Box::new(t.0))))), -TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int2(Box::new(t.0))))), -TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int3(Box::new(t.0))))), -TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int4(Box::new(t.0))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), -TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t.0))))), -TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasStuff(Box::new(t.0))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), -TypeVariant::Nester => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Nester(Box::new(t.0))))), -TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t.0))))), -TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t.0))))), -TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t.0))))), + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint512(Box::new(t.0))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint513(Box::new(t.0))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint514(Box::new(t.0))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str(Box::new(t.0))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str2(Box::new(t.0))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hash(Box::new(t.0))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes1(Box::new(t.0))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes2(Box::new(t.0))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes3(Box::new(t.0))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash1(Box::new(t.0))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash2(Box::new(t.0))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int1(Box::new(t.0))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int2(Box::new(t.0))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int3(Box::new(t.0))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int4(Box::new(t.0))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t.0))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasStuff(Box::new(t.0))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Nester(Box::new(t.0))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t.0))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t.0))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), -TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), -TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), -TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Str(Box::new(t))))), -TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Str2(Box::new(t))))), -TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hash(Box::new(t))))), -TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), -TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), -TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), -TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), -TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), -TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int1(Box::new(t))))), -TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int2(Box::new(t))))), -TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int3(Box::new(t))))), -TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int4(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), -TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), -TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Nester(Box::new(t))))), -TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), -TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), -TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Str(Box::new(t))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Str2(Box::new(t))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hash(Box::new(t))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int1(Box::new(t))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int2(Box::new(t))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int3(Box::new(t))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int4(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Nester(Box::new(t))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::Uint512 => Ok(Self::Uint512(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Uint513 => Ok(Self::Uint513(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Uint514 => Ok(Self::Uint514(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Str => Ok(Self::Str(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Str2 => Ok(Self::Str2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hash => Ok(Self::Hash(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hashes1 => Ok(Self::Hashes1(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hashes2 => Ok(Self::Hashes2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hashes3 => Ok(Self::Hashes3(Box::new(serde_json::from_reader(r)?))), +TypeVariant::OptHash1 => Ok(Self::OptHash1(Box::new(serde_json::from_reader(r)?))), +TypeVariant::OptHash2 => Ok(Self::OptHash2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int1 => Ok(Self::Int1(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int2 => Ok(Self::Int2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int3 => Ok(Self::Int3(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int4 => Ok(Self::Int4(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyStruct => Ok(Self::MyStruct(Box::new(serde_json::from_reader(r)?))), +TypeVariant::LotsOfMyStructs => Ok(Self::LotsOfMyStructs(Box::new(serde_json::from_reader(r)?))), +TypeVariant::HasStuff => Ok(Self::HasStuff(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Color => Ok(Self::Color(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Nester => Ok(Self::Nester(Box::new(serde_json::from_reader(r)?))), +TypeVariant::NesterNestedEnum => Ok(Self::NesterNestedEnum(Box::new(serde_json::from_reader(r)?))), +TypeVariant::NesterNestedStruct => Ok(Self::NesterNestedStruct(Box::new(serde_json::from_reader(r)?))), +TypeVariant::NesterNestedUnion => Ok(Self::NesterNestedUnion(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -4366,3 +4807,35 @@ Self::NesterNestedUnion(_) => TypeVariant::NesterNestedUnion, Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::Uint512(v) => v.write_xdr(w), +Self::Uint513(v) => v.write_xdr(w), +Self::Uint514(v) => v.write_xdr(w), +Self::Str(v) => v.write_xdr(w), +Self::Str2(v) => v.write_xdr(w), +Self::Hash(v) => v.write_xdr(w), +Self::Hashes1(v) => v.write_xdr(w), +Self::Hashes2(v) => v.write_xdr(w), +Self::Hashes3(v) => v.write_xdr(w), +Self::OptHash1(v) => v.write_xdr(w), +Self::OptHash2(v) => v.write_xdr(w), +Self::Int1(v) => v.write_xdr(w), +Self::Int2(v) => v.write_xdr(w), +Self::Int3(v) => v.write_xdr(w), +Self::Int4(v) => v.write_xdr(w), +Self::MyStruct(v) => v.write_xdr(w), +Self::LotsOfMyStructs(v) => v.write_xdr(w), +Self::HasStuff(v) => v.write_xdr(w), +Self::Color(v) => v.write_xdr(w), +Self::Nester(v) => v.write_xdr(w), +Self::NesterNestedEnum(v) => v.write_xdr(w), +Self::NesterNestedStruct(v) => v.write_xdr(w), +Self::NesterNestedUnion(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust/union.x/MyXDR.rs b/spec/output/generator_spec_rust/union.x/MyXDR.rs index 54ad72bea..2cf3d387c 100644 --- a/spec/output/generator_spec_rust/union.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/union.x/MyXDR.rs @@ -51,14 +51,6 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; -/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. -/// -/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from -/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. -/// For more information about Rust's stack size limit, refer to the -/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). -pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; - /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -75,6 +67,9 @@ pub enum Error { #[cfg(feature = "std")] Io(io::Error), DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, } impl PartialEq for Error { @@ -100,6 +95,8 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), _ => None, } } @@ -119,6 +116,9 @@ impl fmt::Display for Error { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), } } } @@ -152,6 +152,14 @@ impl From for Error { } } +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + impl From for () { fn from(_: Error) {} } @@ -190,148 +198,130 @@ where { } -/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. -/// It provides a mechanism to limit recursion depth, and defines the behavior upon -/// entering and leaving a recursion level. -pub trait DepthLimiter { - /// A general error type for any type implementing, or an operation under the guard of - /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded - /// which is returned from `enter`. - type DepthLimiterError; +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, - /// Defines the behavior for entering a new recursion level. - /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. - fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} - /// Defines the behavior for leaving a recursion level. - /// A `DepthLimiterError` is returned if an error occurs. - fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } - /// Wraps a given function `f` with depth limiting guards. - /// It triggers an `enter` before, and a `leave` after the execution of `f`. - /// - /// # Parameters - /// - /// - `f`: The function to be executed under depth limit constraints. - /// - /// # Returns - /// - /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes - /// with an error 3. if error occurs on `leave`. - /// `Ok` otherwise. - fn with_limited_depth(&mut self, f: F) -> core::result::Result - where - F: FnOnce(&mut Self) -> core::result::Result, - { - self.enter()?; - let res = f(self); - self.leave()?; - res + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } } } -/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to -/// recursive read operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. #[cfg(feature = "std")] -pub struct DepthLimitedRead { - pub inner: R, - pub(crate) depth_remaining: u32, +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, } #[cfg(feature = "std")] -impl DepthLimitedRead { - /// Constructs a new `DepthLimitedRead`. +impl Limited { + /// Constructs a new `Limited`. /// - /// - `inner`: The object implementing the `Read` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: R, depth_limit: u32) -> Self { - DepthLimitedRead { - inner, - depth_remaining: depth_limit, - } + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } } -} -#[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedRead { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> core::result::Result<(), Error> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) } else { - return Err(Error::DepthLimitExceeded); + Err(Error::LengthLimitExceeded) } - Ok(()) } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } } } #[cfg(feature = "std")] -impl Read for DepthLimitedRead { +impl Read for Limited { /// Forwards the read operation to the wrapped object. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf) } } -/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to -/// recursive write operations. It maintains a `depth_remaining` state tracking -/// remaining allowed recursion depth. -#[cfg(feature = "std")] -pub struct DepthLimitedWrite { - pub inner: W, - pub(crate) depth_remaining: u32, -} - -#[cfg(feature = "std")] -impl DepthLimitedWrite { - /// Constructs a new `DepthLimitedWrite`. - /// - /// - `inner`: The object implementing the `Write` trait. - /// - `depth_limit`: The maximum allowed recursion depth. - pub fn new(inner: W, depth_limit: u32) -> Self { - DepthLimitedWrite { - inner, - depth_remaining: depth_limit, - } - } -} - #[cfg(feature = "std")] -impl DepthLimiter for DepthLimitedWrite { - type DepthLimiterError = Error; - - /// Decrements the `depth_remaining`. If the depth is already zero, an error is - /// returned indicating that the maximum depth limit has been exceeded. - fn enter(&mut self) -> Result<()> { - if let Some(depth) = self.depth_remaining.checked_sub(1) { - self.depth_remaining = depth; - } else { - return Err(Error::DepthLimitExceeded); - } - Ok(()) +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() } - /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth - /// doesn't exceed the initial depth limit. - fn leave(&mut self) -> core::result::Result<(), Error> { - self.depth_remaining = self.depth_remaining.saturating_add(1); - Ok(()) + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); } } #[cfg(feature = "std")] -impl Write for DepthLimitedWrite { +impl Write for Limited { /// Forwards the write operation to the wrapped object. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.inner.write(buf) @@ -345,17 +335,17 @@ impl Write for DepthLimitedWrite { #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: DepthLimitedRead>, + reader: Limited>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R, depth_limit: u32) -> Self { + fn new(r: R, limits: Limits) -> Self { Self { - reader: DepthLimitedRead { + reader: Limited { inner: BufReader::new(r), - depth_remaining: depth_limit, + limits, }, _s: PhantomData, } @@ -380,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.inner.fill_buf() { + match self.reader.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -417,17 +407,17 @@ where /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result; + fn read_xdr(r: &mut Limited) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr(&mut dec)?; Ok(t) @@ -452,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { + fn read_xdr_to_end(r: &mut Limited) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -468,10 +458,10 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new( + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), - r.depth_remaining, + r.limits.clone(), ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) @@ -492,7 +482,7 @@ where /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -516,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -546,97 +536,69 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(&mut r.inner, r.depth_remaining) + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut DepthLimitedRead, + r: &mut Limited, ) -> ReadXdrIter, Self> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); - ReadXdrIter::new(dec, r.depth_remaining) + ReadXdrIter::new(dec, r.limits.clone()) } - /// Construct the type from the XDR bytes, specifying a depth limit. + /// Construct the type from the XDR bytes. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) - } - - /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. + /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new( + let mut dec = Limited::new( base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), - depth_limit, + limits, ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } - - /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. - /// - /// An error is returned if the bytes are not completely consumed by the - /// deserialization. - #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { - ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) - } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; + fn write_xdr(&self, w: &mut Limited) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { - let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); self.write_xdr(&mut cursor)?; let bytes = cursor.inner.into_inner(); Ok(bytes) } - #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } - #[cfg(feature = "base64")] - fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { - let mut enc = DepthLimitedWrite::new( + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( base64::write::EncoderStringWriter::new(base64::STANDARD), - depth_limit, + limits, ); self.write_xdr(&mut enc)?; let b64 = enc.inner.into_inner(); Ok(b64) } - - #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) - } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -648,9 +610,10 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i32::from_be_bytes(b)) }) @@ -659,17 +622,21 @@ impl ReadXdr for i32 { impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 4]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u32::from_be_bytes(b)) }) @@ -678,17 +645,21 @@ impl ReadXdr for u32 { impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(i64::from_be_bytes(b)) }) @@ -697,17 +668,21 @@ impl ReadXdr for i64 { impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { let mut b = [0u8; 8]; r.with_limited_depth(|r| { + r.consume_len(b.len())?; r.read_exact(&mut b)?; Ok(u64::from_be_bytes(b)) }) @@ -716,43 +691,46 @@ impl ReadXdr for u64 { impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.with_limited_depth(|w| Ok(w.write_all(&b)?)) + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; let b = i == 1; @@ -763,7 +741,7 @@ impl ReadXdr for bool { impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i = u32::from(*self); // true = 1, false = 0 i.write_xdr(w) @@ -773,7 +751,7 @@ impl WriteXdr for bool { impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = u32::read_xdr(r)?; match i { @@ -790,7 +768,7 @@ impl ReadXdr for Option { impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { if let Some(t) = self { 1u32.write_xdr(w)?; @@ -805,39 +783,42 @@ impl WriteXdr for Option { impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut DepthLimitedRead) -> Result { + fn read_xdr(_r: &mut Limited) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; let mut arr = [0u8; N]; r.read_exact(&mut arr)?; - let pad = &mut [0u8; 3][..pad_len(N)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -849,10 +830,13 @@ impl ReadXdr for [u8; N] { impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) } @@ -860,7 +844,7 @@ impl WriteXdr for [u8; N] { impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let mut vec = Vec::with_capacity(N); for _ in 0..N { @@ -875,7 +859,7 @@ impl ReadXdr for [T; N] { impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { for t in self { t.write_xdr(w)?; @@ -1208,17 +1192,21 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1231,14 +1219,18 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -1247,14 +1239,14 @@ impl WriteXdr for VecM { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } - let mut vec = Vec::with_capacity(len as usize); + let mut vec = Vec::new(); for _ in 0..len { let t = T::read_xdr(r)?; vec.push(t); @@ -1267,7 +1259,7 @@ impl ReadXdr for VecM { impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; @@ -1606,17 +1598,21 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -1629,11 +1625,15 @@ impl ReadXdr for BytesM { impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; w.write_all(&[0u8; 3][..pad_len(len as usize)])?; @@ -1989,17 +1989,21 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let len: u32 = u32::read_xdr(r)?; if len > MAX { return Err(Error::LengthExceedsMax); } + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + let mut vec = vec![0u8; len as usize]; r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; + let pad = &mut [0u8; 3][..padding]; r.read_exact(pad)?; if pad.iter().any(|b| *b != 0) { return Err(Error::NonZeroPadding); @@ -2012,14 +2016,18 @@ impl ReadXdr for StringM { impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; len.write_xdr(w)?; + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..padding])?; Ok(()) }) @@ -2043,7 +2051,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -2066,34 +2074,26 @@ where mod tests { use std::io::Cursor; - use super::{ - DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, - DEFAULT_XDR_RW_DEPTH_LIMIT, - }; + use super::*; #[test] pub fn vec_u8_read_without_padding() { let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2103,8 +2103,7 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = - VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2116,11 +2115,8 @@ mod tests { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -2128,34 +2124,29 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) - .unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { let buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { let buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) - .unwrap(); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { let buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -2165,7 +2156,7 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { let buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -2176,10 +2167,7 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -2188,10 +2176,7 @@ mod tests { pub fn arr_u8_write_with_padding() { let mut buf = vec![]; [2u8] - .write_xdr(&mut DepthLimitedWrite::new( - Cursor::new(&mut buf), - DEFAULT_XDR_RW_DEPTH_LIMIT, - )) + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } @@ -2228,19 +2213,18 @@ mod test { #[test] fn depth_limited_read_write_under_the_limit_success() { let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); assert_eq!(a, a_back); } #[test] fn write_over_depth_limit_fail() { - let depth_limit = 3; let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); let res = a.write_xdr(&mut buf); match res { Err(Error::DepthLimitExceeded) => (), @@ -2250,19 +2234,448 @@ mod test { #[test] fn read_over_depth_limit_fail() { - let read_depth_limit = 3; - let write_depth_limit = 5; + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); let a: Option>> = Some(Some(Some(5))); - let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + let mut buf = Limited::new(Vec::new(), write_limits); a.write_xdr(&mut buf).unwrap(); - let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); let res: Result>>> = ReadXdr::read_xdr(&mut dlr); match res { Err(Error::DepthLimitExceeded) => (), _ => panic!("expected DepthLimitExceeded got {res:?}"), } } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2375,7 +2788,7 @@ Self::Multi => "Multi", impl ReadXdr for UnionKey { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let e = i32::read_xdr(r)?; let v: Self = e.try_into()?; @@ -2386,7 +2799,7 @@ Self::Multi => "Multi", impl WriteXdr for UnionKey { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { let i: i32 = (*self).into(); i.write_xdr(w) @@ -2473,7 +2886,7 @@ Self::Multi(_) => UnionKey::Multi, impl ReadXdr for MyUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let dv: UnionKey = ::read_xdr(r)?; #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] @@ -2490,7 +2903,7 @@ UnionKey::Multi => Self::Multi(VecM::::read_xdr(r)?), impl WriteXdr for MyUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.discriminant().write_xdr(w)?; #[allow(clippy::match_same_arms)] @@ -2581,7 +2994,7 @@ Self::V1(_) => 1, impl ReadXdr for IntUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let dv: i32 = ::read_xdr(r)?; #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] @@ -2598,7 +3011,7 @@ Self::V1(_) => 1, impl WriteXdr for IntUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w| { self.discriminant().write_xdr(w)?; #[allow(clippy::match_same_arms)] @@ -2617,8 +3030,8 @@ Self::V1(v) => v.write_xdr(w)?, // #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -#[derive(Debug)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] pub struct IntUnion2(pub IntUnion); impl From for IntUnion { @@ -2644,7 +3057,7 @@ impl AsRef for IntUnion2 { impl ReadXdr for IntUnion2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut DepthLimitedRead) -> Result { + fn read_xdr(r: &mut Limited) -> Result { r.with_limited_depth(|r| { let i = IntUnion::read_xdr(r)?; let v = IntUnion2(i); @@ -2655,7 +3068,7 @@ impl ReadXdr for IntUnion2 { impl WriteXdr for IntUnion2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + fn write_xdr(&self, w: &mut Limited) -> Result<()> { w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2770,7 +3183,7 @@ TypeVariant::IntUnion2, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { match v { TypeVariant::SError => r.with_limited_depth(|r| Ok(Self::SError(Box::new(SError::read_xdr(r)?)))), TypeVariant::Multi => r.with_limited_depth(|r| Ok(Self::Multi(Box::new(Multi::read_xdr(r)?)))), @@ -2782,14 +3195,14 @@ TypeVariant::IntUnion2 => r.with_limited_depth(|r| Ok(Self::IntUnion2(Box::new(I } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2801,67 +3214,80 @@ TypeVariant::IntUnion2 => r.with_limited_depth(|r| Ok(Self::IntUnion2(Box::new(I } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::SError(Box::new(t))))), -TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Multi(Box::new(t))))), -TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), -TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), + TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::SError(Box::new(t))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Multi(Box::new(t))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { match v { - TypeVariant::SError => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::SError(Box::new(t.0))))), -TypeVariant::Multi => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Multi(Box::new(t.0))))), -TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), -TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion(Box::new(t.0))))), -TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion2(Box::new(t.0))))), + TypeVariant::SError => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::SError(Box::new(t.0))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Multi(Box::new(t.0))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion(Box::new(t.0))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion2(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::SError(Box::new(t))))), -TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Multi(Box::new(t))))), -TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), -TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), + TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::SError(Box::new(t))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Multi(Box::new(t))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), } } #[cfg(feature = "std")] - pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } #[cfg(feature = "base64")] - pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::SError => Ok(Self::SError(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Multi => Ok(Self::Multi(Box::new(serde_json::from_reader(r)?))), +TypeVariant::UnionKey => Ok(Self::UnionKey(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnion => Ok(Self::MyUnion(Box::new(serde_json::from_reader(r)?))), +TypeVariant::IntUnion => Ok(Self::IntUnion(Box::new(serde_json::from_reader(r)?))), +TypeVariant::IntUnion2 => Ok(Self::IntUnion2(Box::new(serde_json::from_reader(r)?))), + } + } + #[cfg(feature = "alloc")] #[must_use] #[allow(clippy::too_many_lines)] @@ -2922,3 +3348,18 @@ Self::IntUnion2(_) => TypeVariant::IntUnion2, Self::VARIANTS.iter() } } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::SError(v) => v.write_xdr(w), +Self::Multi(v) => v.write_xdr(w), +Self::UnionKey(v) => v.write_xdr(w), +Self::MyUnion(v) => v.write_xdr(w), +Self::IntUnion(v) => v.write_xdr(w), +Self::IntUnion2(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust_custom_str_impls/block_comments.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/block_comments.x/MyXDR.rs new file mode 100644 index 000000000..cbd950c3d --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/block_comments.x/MyXDR.rs @@ -0,0 +1,2997 @@ +// Module is generated from: +// spec/fixtures/generator/block_comments.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/block_comments.x", "e13131bc4134f38da17b9d5e9f67d2695a69ef98e3ef272833f4c18d0cc88a30") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// AccountFlags is an XDR Enum defines as: +// +// enum AccountFlags +// { // masks for each flag +// AUTH_REQUIRED_FLAG = 0x1 +// }; +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[repr(i32)] +pub enum AccountFlags { + AuthRequiredFlag = 1, +} + +impl AccountFlags { + pub const VARIANTS: [AccountFlags; 1] = [ AccountFlags::AuthRequiredFlag, ]; + pub const VARIANTS_STR: [&'static str; 1] = [ "AuthRequiredFlag", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::AuthRequiredFlag => "AuthRequiredFlag", + } + } + + #[must_use] + pub const fn variants() -> [AccountFlags; 1] { + Self::VARIANTS + } +} + +impl Name for AccountFlags { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } +} + +impl Variants for AccountFlags { + fn variants() -> slice::Iter<'static, AccountFlags> { + Self::VARIANTS.iter() + } +} + +impl Enum for AccountFlags {} + +impl fmt::Display for AccountFlags { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } +} + +impl TryFrom for AccountFlags { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 1 => AccountFlags::AuthRequiredFlag, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } +} + +impl From for i32 { + #[must_use] + fn from(e: AccountFlags) -> Self { + e as Self + } +} + +impl ReadXdr for AccountFlags { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } +} + +impl WriteXdr for AccountFlags { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } +} + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub enum TypeVariant { + AccountFlags, +} + +impl TypeVariant { + pub const VARIANTS: [TypeVariant; 1] = [ TypeVariant::AccountFlags, ]; + pub const VARIANTS_STR: [&'static str; 1] = [ "AccountFlags", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::AccountFlags => "AccountFlags", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 1] { + Self::VARIANTS + } +} + +impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } +} + +impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } +} + +impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "AccountFlags" => Ok(Self::AccountFlags), + _ => Err(Error::Invalid), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), +)] +pub enum Type { + AccountFlags(Box), +} + +impl Type { + pub const VARIANTS: [TypeVariant; 1] = [ TypeVariant::AccountFlags, ]; + pub const VARIANTS_STR: [&'static str; 1] = [ "AccountFlags", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::AccountFlags => r.with_limited_depth(|r| Ok(Self::AccountFlags(Box::new(AccountFlags::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::AccountFlags(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::AccountFlags => Ok(Self::AccountFlags(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::AccountFlags(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::AccountFlags(_) => "AccountFlags", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 1] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::AccountFlags(_) => TypeVariant::AccountFlags, + } + } +} + +impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } +} + +impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } +} + +impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::AccountFlags(v) => v.write_xdr(w), + } + } +} diff --git a/spec/output/generator_spec_rust_custom_str_impls/const.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/const.x/MyXDR.rs new file mode 100644 index 000000000..18b47560b --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/const.x/MyXDR.rs @@ -0,0 +1,2937 @@ +// Module is generated from: +// spec/fixtures/generator/const.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/const.x", "0bff3b37592fcc16cad2fe10b9a72f5d39d033a114917c24e86a9ebd9cda9c37") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// Foo is an XDR Const defines as: +// +// const FOO = 1; +// +pub const FOO: u64 = 1; + +// TestArray is an XDR Typedef defines as: +// +// typedef int TestArray[FOO]; +// +pub type TestArray = [i32; Foo]; + +// TestArray2 is an XDR Typedef defines as: +// +// typedef int TestArray2; +// +pub type TestArray2 = VecM::; + + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") + )] + pub enum TypeVariant { + TestArray, +TestArray2, + } + + impl TypeVariant { + pub const VARIANTS: [TypeVariant; 2] = [ TypeVariant::TestArray, +TypeVariant::TestArray2, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "TestArray", +"TestArray2", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::TestArray => "TestArray", +Self::TestArray2 => "TestArray2", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 2] { + Self::VARIANTS + } + } + + impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "TestArray" => Ok(Self::TestArray), +"TestArray2" => Ok(Self::TestArray2), + _ => Err(Error::Invalid), + } + } + } + + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), + )] + pub enum Type { + TestArray(Box), +TestArray2(Box), + } + + impl Type { + pub const VARIANTS: [TypeVariant; 2] = [ TypeVariant::TestArray, +TypeVariant::TestArray2, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "TestArray", +"TestArray2", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::TestArray => r.with_limited_depth(|r| Ok(Self::TestArray(Box::new(TestArray::read_xdr(r)?)))), +TypeVariant::TestArray2 => r.with_limited_depth(|r| Ok(Self::TestArray2(Box::new(TestArray2::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray(Box::new(t.0))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::TestArray2(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::TestArray => Ok(Self::TestArray(Box::new(serde_json::from_reader(r)?))), +TypeVariant::TestArray2 => Ok(Self::TestArray2(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::TestArray(ref v) => v.as_ref(), +Self::TestArray2(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::TestArray(_) => "TestArray", +Self::TestArray2(_) => "TestArray2", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 2] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::TestArray(_) => TypeVariant::TestArray, +Self::TestArray2(_) => TypeVariant::TestArray2, + } + } + } + + impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::TestArray(v) => v.write_xdr(w), +Self::TestArray2(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust_custom_str_impls/enum.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/enum.x/MyXDR.rs new file mode 100644 index 000000000..31f640596 --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/enum.x/MyXDR.rs @@ -0,0 +1,3327 @@ +// Module is generated from: +// spec/fixtures/generator/enum.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/enum.x", "35cf5e97e2057039640ed260e8b38bb2733a3c3ca8529c93877bdec02a999d7f") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// MessageType is an XDR Enum defines as: +// +// enum MessageType +// { +// ERROR_MSG, +// HELLO, +// DONT_HAVE, +// +// GET_PEERS, // gets a list of peers this guy knows about +// PEERS, +// +// GET_TX_SET, // gets a particular txset by hash +// TX_SET, +// +// GET_VALIDATIONS, // gets validations for a given ledger hash +// VALIDATIONS, +// +// TRANSACTION, //pass on a tx you have heard about +// JSON_TRANSACTION, +// +// // FBA +// GET_FBA_QUORUMSET, +// FBA_QUORUMSET, +// FBA_MESSAGE +// }; +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[repr(i32)] +pub enum MessageType { + ErrorMsg = 0, + Hello = 1, + DontHave = 2, + GetPeers = 3, + Peers = 4, + GetTxSet = 5, + TxSet = 6, + GetValidations = 7, + Validations = 8, + Transaction = 9, + JsonTransaction = 10, + GetFbaQuorumset = 11, + FbaQuorumset = 12, + FbaMessage = 13, +} + + impl MessageType { + pub const VARIANTS: [MessageType; 14] = [ MessageType::ErrorMsg, +MessageType::Hello, +MessageType::DontHave, +MessageType::GetPeers, +MessageType::Peers, +MessageType::GetTxSet, +MessageType::TxSet, +MessageType::GetValidations, +MessageType::Validations, +MessageType::Transaction, +MessageType::JsonTransaction, +MessageType::GetFbaQuorumset, +MessageType::FbaQuorumset, +MessageType::FbaMessage, ]; + pub const VARIANTS_STR: [&'static str; 14] = [ "ErrorMsg", +"Hello", +"DontHave", +"GetPeers", +"Peers", +"GetTxSet", +"TxSet", +"GetValidations", +"Validations", +"Transaction", +"JsonTransaction", +"GetFbaQuorumset", +"FbaQuorumset", +"FbaMessage", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::ErrorMsg => "ErrorMsg", +Self::Hello => "Hello", +Self::DontHave => "DontHave", +Self::GetPeers => "GetPeers", +Self::Peers => "Peers", +Self::GetTxSet => "GetTxSet", +Self::TxSet => "TxSet", +Self::GetValidations => "GetValidations", +Self::Validations => "Validations", +Self::Transaction => "Transaction", +Self::JsonTransaction => "JsonTransaction", +Self::GetFbaQuorumset => "GetFbaQuorumset", +Self::FbaQuorumset => "FbaQuorumset", +Self::FbaMessage => "FbaMessage", + } + } + + #[must_use] + pub const fn variants() -> [MessageType; 14] { + Self::VARIANTS + } + } + + impl Name for MessageType { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for MessageType { + fn variants() -> slice::Iter<'static, MessageType> { + Self::VARIANTS.iter() + } + } + + impl Enum for MessageType {} + + impl fmt::Display for MessageType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl TryFrom for MessageType { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 0 => MessageType::ErrorMsg, +1 => MessageType::Hello, +2 => MessageType::DontHave, +3 => MessageType::GetPeers, +4 => MessageType::Peers, +5 => MessageType::GetTxSet, +6 => MessageType::TxSet, +7 => MessageType::GetValidations, +8 => MessageType::Validations, +9 => MessageType::Transaction, +10 => MessageType::JsonTransaction, +11 => MessageType::GetFbaQuorumset, +12 => MessageType::FbaQuorumset, +13 => MessageType::FbaMessage, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } + } + + impl From for i32 { + #[must_use] + fn from(e: MessageType) -> Self { + e as Self + } + } + + impl ReadXdr for MessageType { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } + } + + impl WriteXdr for MessageType { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } + } + +// Color is an XDR Enum defines as: +// +// enum Color { +// RED=0, +// GREEN=1, +// BLUE=2 +// }; +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[repr(i32)] +pub enum Color { + Red = 0, + Green = 1, + Blue = 2, +} + + impl Color { + pub const VARIANTS: [Color; 3] = [ Color::Red, +Color::Green, +Color::Blue, ]; + pub const VARIANTS_STR: [&'static str; 3] = [ "Red", +"Green", +"Blue", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::Red => "Red", +Self::Green => "Green", +Self::Blue => "Blue", + } + } + + #[must_use] + pub const fn variants() -> [Color; 3] { + Self::VARIANTS + } + } + + impl Name for Color { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Color { + fn variants() -> slice::Iter<'static, Color> { + Self::VARIANTS.iter() + } + } + + impl Enum for Color {} + + impl fmt::Display for Color { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl TryFrom for Color { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 0 => Color::Red, +1 => Color::Green, +2 => Color::Blue, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } + } + + impl From for i32 { + #[must_use] + fn from(e: Color) -> Self { + e as Self + } + } + + impl ReadXdr for Color { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } + } + + impl WriteXdr for Color { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } + } + +// Color2 is an XDR Enum defines as: +// +// enum Color2 { +// RED2=RED, +// GREEN2=1, +// BLUE2=2 +// }; +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +#[repr(i32)] +pub enum Color2 { + Red2 = 0, + Green2 = 1, + Blue2 = 2, +} + + impl Color2 { + pub const VARIANTS: [Color2; 3] = [ Color2::Red2, +Color2::Green2, +Color2::Blue2, ]; + pub const VARIANTS_STR: [&'static str; 3] = [ "Red2", +"Green2", +"Blue2", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::Red2 => "Red2", +Self::Green2 => "Green2", +Self::Blue2 => "Blue2", + } + } + + #[must_use] + pub const fn variants() -> [Color2; 3] { + Self::VARIANTS + } + } + + impl Name for Color2 { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Color2 { + fn variants() -> slice::Iter<'static, Color2> { + Self::VARIANTS.iter() + } + } + + impl Enum for Color2 {} + + impl fmt::Display for Color2 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl TryFrom for Color2 { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 0 => Color2::Red2, +1 => Color2::Green2, +2 => Color2::Blue2, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } + } + + impl From for i32 { + #[must_use] + fn from(e: Color2) -> Self { + e as Self + } + } + + impl ReadXdr for Color2 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } + } + + impl WriteXdr for Color2 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } + } + + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") + )] + pub enum TypeVariant { + MessageType, +Color, +Color2, + } + + impl TypeVariant { + pub const VARIANTS: [TypeVariant; 3] = [ TypeVariant::MessageType, +TypeVariant::Color, +TypeVariant::Color2, ]; + pub const VARIANTS_STR: [&'static str; 3] = [ "MessageType", +"Color", +"Color2", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::MessageType => "MessageType", +Self::Color => "Color", +Self::Color2 => "Color2", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 3] { + Self::VARIANTS + } + } + + impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "MessageType" => Ok(Self::MessageType), +"Color" => Ok(Self::Color), +"Color2" => Ok(Self::Color2), + _ => Err(Error::Invalid), + } + } + } + + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), + )] + pub enum Type { + MessageType(Box), +Color(Box), +Color2(Box), + } + + impl Type { + pub const VARIANTS: [TypeVariant; 3] = [ TypeVariant::MessageType, +TypeVariant::Color, +TypeVariant::Color2, ]; + pub const VARIANTS_STR: [&'static str; 3] = [ "MessageType", +"Color", +"Color2", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::MessageType => r.with_limited_depth(|r| Ok(Self::MessageType(Box::new(MessageType::read_xdr(r)?)))), +TypeVariant::Color => r.with_limited_depth(|r| Ok(Self::Color(Box::new(Color::read_xdr(r)?)))), +TypeVariant::Color2 => r.with_limited_depth(|r| Ok(Self::Color2(Box::new(Color2::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color2(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MessageType(Box::new(t.0))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color2(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Color2(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::MessageType => Ok(Self::MessageType(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Color => Ok(Self::Color(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Color2 => Ok(Self::Color2(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::MessageType(ref v) => v.as_ref(), +Self::Color(ref v) => v.as_ref(), +Self::Color2(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::MessageType(_) => "MessageType", +Self::Color(_) => "Color", +Self::Color2(_) => "Color2", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 3] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::MessageType(_) => TypeVariant::MessageType, +Self::Color(_) => TypeVariant::Color, +Self::Color2(_) => TypeVariant::Color2, + } + } + } + + impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::MessageType(v) => v.write_xdr(w), +Self::Color(v) => v.write_xdr(w), +Self::Color2(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust_custom_str_impls/nesting.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/nesting.x/MyXDR.rs new file mode 100644 index 000000000..1c56daf4e --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/nesting.x/MyXDR.rs @@ -0,0 +1,3277 @@ +// Module is generated from: +// spec/fixtures/generator/nesting.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/nesting.x", "5537949272c11f1bd09cf613a3751668b5018d686a1c2aaa3baa91183ca18f6a") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// UnionKey is an XDR Enum defines as: +// +// enum UnionKey { +// ONE = 1, +// TWO = 2, +// OFFER = 3 +// }; +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +#[repr(i32)] +pub enum UnionKey { + One = 1, + Two = 2, + Offer = 3, +} + + impl UnionKey { + pub const VARIANTS: [UnionKey; 3] = [ UnionKey::One, +UnionKey::Two, +UnionKey::Offer, ]; + pub const VARIANTS_STR: [&'static str; 3] = [ "One", +"Two", +"Offer", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::One => "One", +Self::Two => "Two", +Self::Offer => "Offer", + } + } + + #[must_use] + pub const fn variants() -> [UnionKey; 3] { + Self::VARIANTS + } + } + + impl Name for UnionKey { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for UnionKey { + fn variants() -> slice::Iter<'static, UnionKey> { + Self::VARIANTS.iter() + } + } + + impl Enum for UnionKey {} + + impl fmt::Display for UnionKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl TryFrom for UnionKey { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 1 => UnionKey::One, +2 => UnionKey::Two, +3 => UnionKey::Offer, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } + } + + impl From for i32 { + #[must_use] + fn from(e: UnionKey) -> Self { + e as Self + } + } + + impl ReadXdr for UnionKey { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } + } + + impl WriteXdr for UnionKey { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } + } + +// Foo is an XDR Typedef defines as: +// +// typedef int Foo; +// +pub type Foo = i32; + +// MyUnionOne is an XDR NestedStruct defines as: +// +// struct { +// int someInt; +// } +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +pub struct MyUnionOne { + pub some_int: i32, +} + +impl ReadXdr for MyUnionOne { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + some_int: i32::read_xdr(r)?, + }) + }) + } +} + +impl WriteXdr for MyUnionOne { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.some_int.write_xdr(w)?; + Ok(()) + }) + } +} + +// MyUnionTwo is an XDR NestedStruct defines as: +// +// struct { +// int someInt; +// Foo foo; +// } +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +pub struct MyUnionTwo { + pub some_int: i32, + pub foo: i32, +} + + impl ReadXdr for MyUnionTwo { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + some_int: i32::read_xdr(r)?, +foo: i32::read_xdr(r)?, + }) + }) + } + } + + impl WriteXdr for MyUnionTwo { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.some_int.write_xdr(w)?; +self.foo.write_xdr(w)?; + Ok(()) + }) + } + } + +// MyUnion is an XDR Union defines as: +// +// union MyUnion switch (UnionKey type) +// { +// case ONE: +// struct { +// int someInt; +// } one; +// +// case TWO: +// struct { +// int someInt; +// Foo foo; +// } two; +// +// case OFFER: +// void; +// }; +// +// union with discriminant UnionKey +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +#[allow(clippy::large_enum_variant)] +pub enum MyUnion { + One(MyUnionOne), + Two(MyUnionTwo), + Offer, +} + + impl MyUnion { + pub const VARIANTS: [UnionKey; 3] = [ + UnionKey::One, +UnionKey::Two, +UnionKey::Offer, + ]; + pub const VARIANTS_STR: [&'static str; 3] = [ + "One", +"Two", +"Offer", + ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::One(_) => "One", +Self::Two(_) => "Two", +Self::Offer => "Offer", + } + } + + #[must_use] + pub const fn discriminant(&self) -> UnionKey { + #[allow(clippy::match_same_arms)] + match self { + Self::One(_) => UnionKey::One, +Self::Two(_) => UnionKey::Two, +Self::Offer => UnionKey::Offer, + } + } + + #[must_use] + pub const fn variants() -> [UnionKey; 3] { + Self::VARIANTS + } + } + + impl Name for MyUnion { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Discriminant for MyUnion { + #[must_use] + fn discriminant(&self) -> UnionKey { + Self::discriminant(self) + } + } + + impl Variants for MyUnion { + fn variants() -> slice::Iter<'static, UnionKey> { + Self::VARIANTS.iter() + } + } + + impl Union for MyUnion {} + + impl ReadXdr for MyUnion { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let dv: UnionKey = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + UnionKey::One => Self::One(MyUnionOne::read_xdr(r)?), +UnionKey::Two => Self::Two(MyUnionTwo::read_xdr(r)?), +UnionKey::Offer => Self::Offer, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) + } + } + + impl WriteXdr for MyUnion { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::One(v) => v.write_xdr(w)?, +Self::Two(v) => v.write_xdr(w)?, +Self::Offer => ().write_xdr(w)?, + }; + Ok(()) + }) + } + } + + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") + )] + pub enum TypeVariant { + UnionKey, +Foo, +MyUnion, +MyUnionOne, +MyUnionTwo, + } + + impl TypeVariant { + pub const VARIANTS: [TypeVariant; 5] = [ TypeVariant::UnionKey, +TypeVariant::Foo, +TypeVariant::MyUnion, +TypeVariant::MyUnionOne, +TypeVariant::MyUnionTwo, ]; + pub const VARIANTS_STR: [&'static str; 5] = [ "UnionKey", +"Foo", +"MyUnion", +"MyUnionOne", +"MyUnionTwo", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::UnionKey => "UnionKey", +Self::Foo => "Foo", +Self::MyUnion => "MyUnion", +Self::MyUnionOne => "MyUnionOne", +Self::MyUnionTwo => "MyUnionTwo", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 5] { + Self::VARIANTS + } + } + + impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "UnionKey" => Ok(Self::UnionKey), +"Foo" => Ok(Self::Foo), +"MyUnion" => Ok(Self::MyUnion), +"MyUnionOne" => Ok(Self::MyUnionOne), +"MyUnionTwo" => Ok(Self::MyUnionTwo), + _ => Err(Error::Invalid), + } + } + } + + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), + )] + pub enum Type { + UnionKey(Box), +Foo(Box), +MyUnion(Box), +MyUnionOne(Box), +MyUnionTwo(Box), + } + + impl Type { + pub const VARIANTS: [TypeVariant; 5] = [ TypeVariant::UnionKey, +TypeVariant::Foo, +TypeVariant::MyUnion, +TypeVariant::MyUnionOne, +TypeVariant::MyUnionTwo, ]; + pub const VARIANTS_STR: [&'static str; 5] = [ "UnionKey", +"Foo", +"MyUnion", +"MyUnionOne", +"MyUnionTwo", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::UnionKey => r.with_limited_depth(|r| Ok(Self::UnionKey(Box::new(UnionKey::read_xdr(r)?)))), +TypeVariant::Foo => r.with_limited_depth(|r| Ok(Self::Foo(Box::new(Foo::read_xdr(r)?)))), +TypeVariant::MyUnion => r.with_limited_depth(|r| Ok(Self::MyUnion(Box::new(MyUnion::read_xdr(r)?)))), +TypeVariant::MyUnionOne => r.with_limited_depth(|r| Ok(Self::MyUnionOne(Box::new(MyUnionOne::read_xdr(r)?)))), +TypeVariant::MyUnionTwo => r.with_limited_depth(|r| Ok(Self::MyUnionTwo(Box::new(MyUnionTwo::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Foo(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Foo(Box::new(t.0))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t.0))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Foo(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::UnionKey => Ok(Self::UnionKey(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Foo => Ok(Self::Foo(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnion => Ok(Self::MyUnion(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnionOne => Ok(Self::MyUnionOne(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnionTwo => Ok(Self::MyUnionTwo(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::UnionKey(ref v) => v.as_ref(), +Self::Foo(ref v) => v.as_ref(), +Self::MyUnion(ref v) => v.as_ref(), +Self::MyUnionOne(ref v) => v.as_ref(), +Self::MyUnionTwo(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::UnionKey(_) => "UnionKey", +Self::Foo(_) => "Foo", +Self::MyUnion(_) => "MyUnion", +Self::MyUnionOne(_) => "MyUnionOne", +Self::MyUnionTwo(_) => "MyUnionTwo", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 5] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::UnionKey(_) => TypeVariant::UnionKey, +Self::Foo(_) => TypeVariant::Foo, +Self::MyUnion(_) => TypeVariant::MyUnion, +Self::MyUnionOne(_) => TypeVariant::MyUnionOne, +Self::MyUnionTwo(_) => TypeVariant::MyUnionTwo, + } + } + } + + impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::UnionKey(v) => v.write_xdr(w), +Self::Foo(v) => v.write_xdr(w), +Self::MyUnion(v) => v.write_xdr(w), +Self::MyUnionOne(v) => v.write_xdr(w), +Self::MyUnionTwo(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust_custom_str_impls/optional.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/optional.x/MyXDR.rs new file mode 100644 index 000000000..e7606d38b --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/optional.x/MyXDR.rs @@ -0,0 +1,2968 @@ +// Module is generated from: +// spec/fixtures/generator/optional.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/optional.x", "3241e832fcf00bca4315ecb6c259621dafb0e302a63a993f5504b0b5cebb6bd7") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// Arr is an XDR Typedef defines as: +// +// typedef int Arr[2]; +// +pub type Arr = [i32; 2]; + +// HasOptions is an XDR Struct defines as: +// +// struct HasOptions +// { +// int* firstOption; +// int *secondOption; +// Arr *thirdOption; +// }; +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +pub struct HasOptions { + pub first_option: Option, + pub second_option: Option, + pub third_option: Option, +} + + impl ReadXdr for HasOptions { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + first_option: Option::::read_xdr(r)?, +second_option: Option::::read_xdr(r)?, +third_option: Option::::read_xdr(r)?, + }) + }) + } + } + + impl WriteXdr for HasOptions { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.first_option.write_xdr(w)?; +self.second_option.write_xdr(w)?; +self.third_option.write_xdr(w)?; + Ok(()) + }) + } + } + + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") + )] + pub enum TypeVariant { + Arr, +HasOptions, + } + + impl TypeVariant { + pub const VARIANTS: [TypeVariant; 2] = [ TypeVariant::Arr, +TypeVariant::HasOptions, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "Arr", +"HasOptions", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::Arr => "Arr", +Self::HasOptions => "HasOptions", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 2] { + Self::VARIANTS + } + } + + impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "Arr" => Ok(Self::Arr), +"HasOptions" => Ok(Self::HasOptions), + _ => Err(Error::Invalid), + } + } + } + + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), + )] + pub enum Type { + Arr(Box), +HasOptions(Box), + } + + impl Type { + pub const VARIANTS: [TypeVariant; 2] = [ TypeVariant::Arr, +TypeVariant::HasOptions, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "Arr", +"HasOptions", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::Arr => r.with_limited_depth(|r| Ok(Self::Arr(Box::new(Arr::read_xdr(r)?)))), +TypeVariant::HasOptions => r.with_limited_depth(|r| Ok(Self::HasOptions(Box::new(HasOptions::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Arr(Box::new(t))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Arr(Box::new(t.0))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasOptions(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Arr(Box::new(t))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::Arr => Ok(Self::Arr(Box::new(serde_json::from_reader(r)?))), +TypeVariant::HasOptions => Ok(Self::HasOptions(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::Arr(ref v) => v.as_ref(), +Self::HasOptions(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::Arr(_) => "Arr", +Self::HasOptions(_) => "HasOptions", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 2] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::Arr(_) => TypeVariant::Arr, +Self::HasOptions(_) => TypeVariant::HasOptions, + } + } + } + + impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::Arr(v) => v.write_xdr(w), +Self::HasOptions(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust_custom_str_impls/struct.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/struct.x/MyXDR.rs new file mode 100644 index 000000000..c569fd2de --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/struct.x/MyXDR.rs @@ -0,0 +1,2976 @@ +// Module is generated from: +// spec/fixtures/generator/struct.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/struct.x", "c6911a83390e3b499c078fd0c579132eacce88a4a0538d3b8b5e57747a58db4a") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// Int64 is an XDR Typedef defines as: +// +// typedef hyper int64; +// +pub type Int64 = i64; + +// MyStruct is an XDR Struct defines as: +// +// struct MyStruct +// { +// int someInt; +// int64 aBigInt; +// opaque someOpaque[10]; +// string someString<>; +// string maxString<100>; +// }; +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +pub struct MyStruct { + pub some_int: i32, + pub a_big_int: i64, + pub some_opaque: [u8; 10], + pub some_string: StringM, + pub max_string: StringM::<100>, +} + + impl ReadXdr for MyStruct { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + some_int: i32::read_xdr(r)?, +a_big_int: i64::read_xdr(r)?, +some_opaque: <[u8; 10]>::read_xdr(r)?, +some_string: StringM::read_xdr(r)?, +max_string: StringM::<100>::read_xdr(r)?, + }) + }) + } + } + + impl WriteXdr for MyStruct { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.some_int.write_xdr(w)?; +self.a_big_int.write_xdr(w)?; +self.some_opaque.write_xdr(w)?; +self.some_string.write_xdr(w)?; +self.max_string.write_xdr(w)?; + Ok(()) + }) + } + } + + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") + )] + pub enum TypeVariant { + Int64, +MyStruct, + } + + impl TypeVariant { + pub const VARIANTS: [TypeVariant; 2] = [ TypeVariant::Int64, +TypeVariant::MyStruct, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "Int64", +"MyStruct", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::Int64 => "Int64", +Self::MyStruct => "MyStruct", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 2] { + Self::VARIANTS + } + } + + impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "Int64" => Ok(Self::Int64), +"MyStruct" => Ok(Self::MyStruct), + _ => Err(Error::Invalid), + } + } + } + + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), + )] + pub enum Type { + Int64(Box), +MyStruct(Box), + } + + impl Type { + pub const VARIANTS: [TypeVariant; 2] = [ TypeVariant::Int64, +TypeVariant::MyStruct, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "Int64", +"MyStruct", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::Int64 => r.with_limited_depth(|r| Ok(Self::Int64(Box::new(Int64::read_xdr(r)?)))), +TypeVariant::MyStruct => r.with_limited_depth(|r| Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int64(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int64(Box::new(t.0))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int64(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::Int64 => Ok(Self::Int64(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyStruct => Ok(Self::MyStruct(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::Int64(ref v) => v.as_ref(), +Self::MyStruct(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::Int64(_) => "Int64", +Self::MyStruct(_) => "MyStruct", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 2] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::Int64(_) => TypeVariant::Int64, +Self::MyStruct(_) => TypeVariant::MyStruct, + } + } + } + + impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::Int64(v) => v.write_xdr(w), +Self::MyStruct(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust_custom_str_impls/test.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/test.x/MyXDR.rs new file mode 100644 index 000000000..08c843587 --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/test.x/MyXDR.rs @@ -0,0 +1,4841 @@ +// Module is generated from: +// spec/fixtures/generator/test.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/test.x", "d29a98a6a3b9bf533a3e6712d928e0bed655e0f462ac4dae810c65d52ca9af41") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// Uint512 is an XDR Typedef defines as: +// +// typedef opaque uint512[64]; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +pub struct Uint512(pub [u8; 64]); + +impl core::fmt::Debug for Uint512 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let v = &self.0; + write!(f, "Uint512(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} +impl core::fmt::Display for Uint512 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let v = &self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for Uint512 { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} +impl From for [u8; 64] { + #[must_use] + fn from(x: Uint512) -> Self { + x.0 + } +} + +impl From<[u8; 64]> for Uint512 { + #[must_use] + fn from(x: [u8; 64]) -> Self { + Uint512(x) + } +} + +impl AsRef<[u8; 64]> for Uint512 { + #[must_use] + fn as_ref(&self) -> &[u8; 64] { + &self.0 + } +} + +impl ReadXdr for Uint512 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = <[u8; 64]>::read_xdr(r)?; + let v = Uint512(i); + Ok(v) + }) + } +} + +impl WriteXdr for Uint512 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Uint512 { + #[must_use] + pub fn as_slice(&self) -> &[u8] { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for Uint512 { + type Error = Error; + fn try_from(x: Vec) -> Result { + x.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Uint512 { + type Error = Error; + fn try_from(x: &Vec) -> Result { + x.as_slice().try_into() + } +} + +impl TryFrom<&[u8]> for Uint512 { + type Error = Error; + fn try_from(x: &[u8]) -> Result { + Ok(Uint512(x.try_into()?)) + } +} + +impl AsRef<[u8]> for Uint512 { + #[must_use] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +// Uint513 is an XDR Typedef defines as: +// +// typedef opaque uint513<64>; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[derive(Default)] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct Uint513(pub BytesM::<64>); + +impl From for BytesM::<64> { + #[must_use] + fn from(x: Uint513) -> Self { + x.0 + } +} + +impl From> for Uint513 { + #[must_use] + fn from(x: BytesM::<64>) -> Self { + Uint513(x) + } +} + +impl AsRef> for Uint513 { + #[must_use] + fn as_ref(&self) -> &BytesM::<64> { + &self.0 + } +} + +impl ReadXdr for Uint513 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = BytesM::<64>::read_xdr(r)?; + let v = Uint513(i); + Ok(v) + }) + } +} + +impl WriteXdr for Uint513 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Deref for Uint513 { + type Target = BytesM::<64>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Vec { + #[must_use] + fn from(x: Uint513) -> Self { + x.0.0 + } +} + +impl TryFrom> for Uint513 { + type Error = Error; + fn try_from(x: Vec) -> Result { + Ok(Uint513(x.try_into()?)) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Uint513 { + type Error = Error; + fn try_from(x: &Vec) -> Result { + Ok(Uint513(x.try_into()?)) + } +} + +impl AsRef> for Uint513 { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0.0 + } +} + +impl AsRef<[u8]> for Uint513 { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + &self.0.0 + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.0 + } +} + +// Uint514 is an XDR Typedef defines as: +// +// typedef opaque uint514<>; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[derive(Default)] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct Uint514(pub BytesM); + +impl From for BytesM { + #[must_use] + fn from(x: Uint514) -> Self { + x.0 + } +} + +impl From for Uint514 { + #[must_use] + fn from(x: BytesM) -> Self { + Uint514(x) + } +} + +impl AsRef for Uint514 { + #[must_use] + fn as_ref(&self) -> &BytesM { + &self.0 + } +} + +impl ReadXdr for Uint514 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = BytesM::read_xdr(r)?; + let v = Uint514(i); + Ok(v) + }) + } +} + +impl WriteXdr for Uint514 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Deref for Uint514 { + type Target = BytesM; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Vec { + #[must_use] + fn from(x: Uint514) -> Self { + x.0.0 + } +} + +impl TryFrom> for Uint514 { + type Error = Error; + fn try_from(x: Vec) -> Result { + Ok(Uint514(x.try_into()?)) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Uint514 { + type Error = Error; + fn try_from(x: &Vec) -> Result { + Ok(Uint514(x.try_into()?)) + } +} + +impl AsRef> for Uint514 { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0.0 + } +} + +impl AsRef<[u8]> for Uint514 { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + &self.0.0 + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.0 + } +} + +// Str is an XDR Typedef defines as: +// +// typedef string str<64>; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[derive(Default)] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct Str(pub StringM::<64>); + +impl From for StringM::<64> { + #[must_use] + fn from(x: Str) -> Self { + x.0 + } +} + +impl From> for Str { + #[must_use] + fn from(x: StringM::<64>) -> Self { + Str(x) + } +} + +impl AsRef> for Str { + #[must_use] + fn as_ref(&self) -> &StringM::<64> { + &self.0 + } +} + +impl ReadXdr for Str { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = StringM::<64>::read_xdr(r)?; + let v = Str(i); + Ok(v) + }) + } +} + +impl WriteXdr for Str { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Deref for Str { + type Target = StringM::<64>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Vec { + #[must_use] + fn from(x: Str) -> Self { + x.0.0 + } +} + +impl TryFrom> for Str { + type Error = Error; + fn try_from(x: Vec) -> Result { + Ok(Str(x.try_into()?)) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Str { + type Error = Error; + fn try_from(x: &Vec) -> Result { + Ok(Str(x.try_into()?)) + } +} + +impl AsRef> for Str { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0.0 + } +} + +impl AsRef<[u8]> for Str { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + &self.0.0 + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.0 + } +} + +// Str2 is an XDR Typedef defines as: +// +// typedef string str2<>; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[derive(Default)] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct Str2(pub StringM); + +impl From for StringM { + #[must_use] + fn from(x: Str2) -> Self { + x.0 + } +} + +impl From for Str2 { + #[must_use] + fn from(x: StringM) -> Self { + Str2(x) + } +} + +impl AsRef for Str2 { + #[must_use] + fn as_ref(&self) -> &StringM { + &self.0 + } +} + +impl ReadXdr for Str2 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = StringM::read_xdr(r)?; + let v = Str2(i); + Ok(v) + }) + } +} + +impl WriteXdr for Str2 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Deref for Str2 { + type Target = StringM; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Vec { + #[must_use] + fn from(x: Str2) -> Self { + x.0.0 + } +} + +impl TryFrom> for Str2 { + type Error = Error; + fn try_from(x: Vec) -> Result { + Ok(Str2(x.try_into()?)) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Str2 { + type Error = Error; + fn try_from(x: &Vec) -> Result { + Ok(Str2(x.try_into()?)) + } +} + +impl AsRef> for Str2 { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0.0 + } +} + +impl AsRef<[u8]> for Str2 { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + &self.0.0 + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.0 + } +} + +// Hash is an XDR Typedef defines as: +// +// typedef opaque Hash[32]; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +pub struct Hash(pub [u8; 32]); + +impl core::fmt::Debug for Hash { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let v = &self.0; + write!(f, "Hash(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} +impl core::fmt::Display for Hash { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let v = &self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for Hash { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} +impl From for [u8; 32] { + #[must_use] + fn from(x: Hash) -> Self { + x.0 + } +} + +impl From<[u8; 32]> for Hash { + #[must_use] + fn from(x: [u8; 32]) -> Self { + Hash(x) + } +} + +impl AsRef<[u8; 32]> for Hash { + #[must_use] + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl ReadXdr for Hash { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = <[u8; 32]>::read_xdr(r)?; + let v = Hash(i); + Ok(v) + }) + } +} + +impl WriteXdr for Hash { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Hash { + #[must_use] + pub fn as_slice(&self) -> &[u8] { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for Hash { + type Error = Error; + fn try_from(x: Vec) -> Result { + x.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Hash { + type Error = Error; + fn try_from(x: &Vec) -> Result { + x.as_slice().try_into() + } +} + +impl TryFrom<&[u8]> for Hash { + type Error = Error; + fn try_from(x: &[u8]) -> Result { + Ok(Hash(x.try_into()?)) + } +} + +impl AsRef<[u8]> for Hash { + #[must_use] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +// Hashes1 is an XDR Typedef defines as: +// +// typedef Hash Hashes1[12]; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct Hashes1(pub [Hash; 12]); + +impl From for [Hash; 12] { + #[must_use] + fn from(x: Hashes1) -> Self { + x.0 + } +} + +impl From<[Hash; 12]> for Hashes1 { + #[must_use] + fn from(x: [Hash; 12]) -> Self { + Hashes1(x) + } +} + +impl AsRef<[Hash; 12]> for Hashes1 { + #[must_use] + fn as_ref(&self) -> &[Hash; 12] { + &self.0 + } +} + +impl ReadXdr for Hashes1 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = <[Hash; 12]>::read_xdr(r)?; + let v = Hashes1(i); + Ok(v) + }) + } +} + +impl WriteXdr for Hashes1 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Hashes1 { + #[must_use] + pub fn as_slice(&self) -> &[Hash] { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for Hashes1 { + type Error = Error; + fn try_from(x: Vec) -> Result { + x.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Hashes1 { + type Error = Error; + fn try_from(x: &Vec) -> Result { + x.as_slice().try_into() + } +} + +impl TryFrom<&[Hash]> for Hashes1 { + type Error = Error; + fn try_from(x: &[Hash]) -> Result { + Ok(Hashes1(x.try_into()?)) + } +} + +impl AsRef<[Hash]> for Hashes1 { + #[must_use] + fn as_ref(&self) -> &[Hash] { + &self.0 + } +} + +// Hashes2 is an XDR Typedef defines as: +// +// typedef Hash Hashes2<12>; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[derive(Default)] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct Hashes2(pub VecM::); + +impl From for VecM:: { + #[must_use] + fn from(x: Hashes2) -> Self { + x.0 + } +} + +impl From> for Hashes2 { + #[must_use] + fn from(x: VecM::) -> Self { + Hashes2(x) + } +} + +impl AsRef> for Hashes2 { + #[must_use] + fn as_ref(&self) -> &VecM:: { + &self.0 + } +} + +impl ReadXdr for Hashes2 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = VecM::::read_xdr(r)?; + let v = Hashes2(i); + Ok(v) + }) + } +} + +impl WriteXdr for Hashes2 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Deref for Hashes2 { + type Target = VecM::; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Vec { + #[must_use] + fn from(x: Hashes2) -> Self { + x.0.0 + } +} + +impl TryFrom> for Hashes2 { + type Error = Error; + fn try_from(x: Vec) -> Result { + Ok(Hashes2(x.try_into()?)) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Hashes2 { + type Error = Error; + fn try_from(x: &Vec) -> Result { + Ok(Hashes2(x.try_into()?)) + } +} + +impl AsRef> for Hashes2 { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0.0 + } +} + +impl AsRef<[Hash]> for Hashes2 { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[Hash] { + &self.0.0 + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[Hash] { + self.0.0 + } +} + +// Hashes3 is an XDR Typedef defines as: +// +// typedef Hash Hashes3<>; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[derive(Default)] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct Hashes3(pub VecM::); + +impl From for VecM:: { + #[must_use] + fn from(x: Hashes3) -> Self { + x.0 + } +} + +impl From> for Hashes3 { + #[must_use] + fn from(x: VecM::) -> Self { + Hashes3(x) + } +} + +impl AsRef> for Hashes3 { + #[must_use] + fn as_ref(&self) -> &VecM:: { + &self.0 + } +} + +impl ReadXdr for Hashes3 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = VecM::::read_xdr(r)?; + let v = Hashes3(i); + Ok(v) + }) + } +} + +impl WriteXdr for Hashes3 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +impl Deref for Hashes3 { + type Target = VecM::; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Vec { + #[must_use] + fn from(x: Hashes3) -> Self { + x.0.0 + } +} + +impl TryFrom> for Hashes3 { + type Error = Error; + fn try_from(x: Vec) -> Result { + Ok(Hashes3(x.try_into()?)) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for Hashes3 { + type Error = Error; + fn try_from(x: &Vec) -> Result { + Ok(Hashes3(x.try_into()?)) + } +} + +impl AsRef> for Hashes3 { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0.0 + } +} + +impl AsRef<[Hash]> for Hashes3 { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[Hash] { + &self.0.0 + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[Hash] { + self.0.0 + } +} + +// OptHash1 is an XDR Typedef defines as: +// +// typedef Hash *optHash1; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct OptHash1(pub Option); + +impl From for Option { + #[must_use] + fn from(x: OptHash1) -> Self { + x.0 + } +} + +impl From> for OptHash1 { + #[must_use] + fn from(x: Option) -> Self { + OptHash1(x) + } +} + +impl AsRef> for OptHash1 { + #[must_use] + fn as_ref(&self) -> &Option { + &self.0 + } +} + +impl ReadXdr for OptHash1 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = Option::::read_xdr(r)?; + let v = OptHash1(i); + Ok(v) + }) + } +} + +impl WriteXdr for OptHash1 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +// OptHash2 is an XDR Typedef defines as: +// +// typedef Hash* optHash2; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct OptHash2(pub Option); + +impl From for Option { + #[must_use] + fn from(x: OptHash2) -> Self { + x.0 + } +} + +impl From> for OptHash2 { + #[must_use] + fn from(x: Option) -> Self { + OptHash2(x) + } +} + +impl AsRef> for OptHash2 { + #[must_use] + fn as_ref(&self) -> &Option { + &self.0 + } +} + +impl ReadXdr for OptHash2 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = Option::::read_xdr(r)?; + let v = OptHash2(i); + Ok(v) + }) + } +} + +impl WriteXdr for OptHash2 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + +// Int1 is an XDR Typedef defines as: +// +// typedef int int1; +// +pub type Int1 = i32; + +// Int2 is an XDR Typedef defines as: +// +// typedef hyper int2; +// +pub type Int2 = i64; + +// Int3 is an XDR Typedef defines as: +// +// typedef unsigned int int3; +// +pub type Int3 = u32; + +// Int4 is an XDR Typedef defines as: +// +// typedef unsigned hyper int4; +// +pub type Int4 = u64; + +// MyStruct is an XDR Struct defines as: +// +// struct MyStruct +// { +// uint512 field1; +// optHash1 field2; +// int1 field3; +// unsigned int field4; +// float field5; +// double field6; +// bool field7; +// }; +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +pub struct MyStruct { + pub field1: Uint512, + pub field2: OptHash1, + pub field3: i32, + pub field4: u32, + pub field5: f32, + pub field6: f64, + pub field7: bool, +} + + impl ReadXdr for MyStruct { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + field1: Uint512::read_xdr(r)?, +field2: OptHash1::read_xdr(r)?, +field3: i32::read_xdr(r)?, +field4: u32::read_xdr(r)?, +field5: f32::read_xdr(r)?, +field6: f64::read_xdr(r)?, +field7: bool::read_xdr(r)?, + }) + }) + } + } + + impl WriteXdr for MyStruct { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.field1.write_xdr(w)?; +self.field2.write_xdr(w)?; +self.field3.write_xdr(w)?; +self.field4.write_xdr(w)?; +self.field5.write_xdr(w)?; +self.field6.write_xdr(w)?; +self.field7.write_xdr(w)?; + Ok(()) + }) + } + } + +// LotsOfMyStructs is an XDR Struct defines as: +// +// struct LotsOfMyStructs +// { +// MyStruct members<>; +// }; +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +pub struct LotsOfMyStructs { + pub members: VecM::, +} + +impl ReadXdr for LotsOfMyStructs { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + members: VecM::::read_xdr(r)?, + }) + }) + } +} + +impl WriteXdr for LotsOfMyStructs { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.members.write_xdr(w)?; + Ok(()) + }) + } +} + +// HasStuff is an XDR Struct defines as: +// +// struct HasStuff +// { +// LotsOfMyStructs data; +// }; +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +pub struct HasStuff { + pub data: LotsOfMyStructs, +} + +impl ReadXdr for HasStuff { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + data: LotsOfMyStructs::read_xdr(r)?, + }) + }) + } +} + +impl WriteXdr for HasStuff { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.data.write_xdr(w)?; + Ok(()) + }) + } +} + +// Color is an XDR Enum defines as: +// +// enum Color { +// RED, +// BLUE = 5, +// GREEN +// }; +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[repr(i32)] +pub enum Color { + Red = 0, + Blue = 5, + Green = 6, +} + + impl Color { + pub const VARIANTS: [Color; 3] = [ Color::Red, +Color::Blue, +Color::Green, ]; + pub const VARIANTS_STR: [&'static str; 3] = [ "Red", +"Blue", +"Green", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::Red => "Red", +Self::Blue => "Blue", +Self::Green => "Green", + } + } + + #[must_use] + pub const fn variants() -> [Color; 3] { + Self::VARIANTS + } + } + + impl Name for Color { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Color { + fn variants() -> slice::Iter<'static, Color> { + Self::VARIANTS.iter() + } + } + + impl Enum for Color {} + + impl fmt::Display for Color { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl TryFrom for Color { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 0 => Color::Red, +5 => Color::Blue, +6 => Color::Green, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } + } + + impl From for i32 { + #[must_use] + fn from(e: Color) -> Self { + e as Self + } + } + + impl ReadXdr for Color { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } + } + + impl WriteXdr for Color { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } + } + +// Foo is an XDR Const defines as: +// +// const FOO = 1244; +// +pub const FOO: u64 = 1244; + +// Bar is an XDR Const defines as: +// +// const BAR = FOO; +// +pub const BAR: u64 = FOO; + +// NesterNestedEnum is an XDR NestedEnum defines as: +// +// enum { +// BLAH_1, +// BLAH_2 +// } +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[repr(i32)] +pub enum NesterNestedEnum { + 1 = 0, + 2 = 1, +} + + impl NesterNestedEnum { + pub const VARIANTS: [NesterNestedEnum; 2] = [ NesterNestedEnum::1, +NesterNestedEnum::2, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "1", +"2", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::1 => "1", +Self::2 => "2", + } + } + + #[must_use] + pub const fn variants() -> [NesterNestedEnum; 2] { + Self::VARIANTS + } + } + + impl Name for NesterNestedEnum { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for NesterNestedEnum { + fn variants() -> slice::Iter<'static, NesterNestedEnum> { + Self::VARIANTS.iter() + } + } + + impl Enum for NesterNestedEnum {} + + impl fmt::Display for NesterNestedEnum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl TryFrom for NesterNestedEnum { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 0 => NesterNestedEnum::1, +1 => NesterNestedEnum::2, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } + } + + impl From for i32 { + #[must_use] + fn from(e: NesterNestedEnum) -> Self { + e as Self + } + } + + impl ReadXdr for NesterNestedEnum { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } + } + + impl WriteXdr for NesterNestedEnum { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } + } + +// NesterNestedStruct is an XDR NestedStruct defines as: +// +// struct { +// int blah; +// } +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +pub struct NesterNestedStruct { + pub blah: i32, +} + +impl ReadXdr for NesterNestedStruct { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + blah: i32::read_xdr(r)?, + }) + }) + } +} + +impl WriteXdr for NesterNestedStruct { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.blah.write_xdr(w)?; + Ok(()) + }) + } +} + +// NesterNestedUnion is an XDR NestedUnion defines as: +// +// union switch (Color color) { +// case RED: +// void; +// default: +// int blah2; +// } +// +// union with discriminant Color +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[allow(clippy::large_enum_variant)] +pub enum NesterNestedUnion { + Red, +} + +impl NesterNestedUnion { + pub const VARIANTS: [Color; 1] = [ + Color::Red, + ]; + pub const VARIANTS_STR: [&'static str; 1] = [ + "Red", + ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::Red => "Red", + } + } + + #[must_use] + pub const fn discriminant(&self) -> Color { + #[allow(clippy::match_same_arms)] + match self { + Self::Red => Color::Red, + } + } + + #[must_use] + pub const fn variants() -> [Color; 1] { + Self::VARIANTS + } +} + +impl Name for NesterNestedUnion { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } +} + +impl Discriminant for NesterNestedUnion { + #[must_use] + fn discriminant(&self) -> Color { + Self::discriminant(self) + } +} + +impl Variants for NesterNestedUnion { + fn variants() -> slice::Iter<'static, Color> { + Self::VARIANTS.iter() + } +} + +impl Union for NesterNestedUnion {} + +impl ReadXdr for NesterNestedUnion { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let dv: Color = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + Color::Red => Self::Red, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) + } +} + +impl WriteXdr for NesterNestedUnion { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::Red => ().write_xdr(w)?, + }; + Ok(()) + }) + } +} + +// Nester is an XDR Struct defines as: +// +// struct Nester +// { +// enum { +// BLAH_1, +// BLAH_2 +// } nestedEnum; +// +// struct { +// int blah; +// } nestedStruct; +// +// union switch (Color color) { +// case RED: +// void; +// default: +// int blah2; +// } nestedUnion; +// +// +// }; +// +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +pub struct Nester { + pub nested_enum: NesterNestedEnum, + pub nested_struct: NesterNestedStruct, + pub nested_union: NesterNestedUnion, +} + + impl ReadXdr for Nester { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + nested_enum: NesterNestedEnum::read_xdr(r)?, +nested_struct: NesterNestedStruct::read_xdr(r)?, +nested_union: NesterNestedUnion::read_xdr(r)?, + }) + }) + } + } + + impl WriteXdr for Nester { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.nested_enum.write_xdr(w)?; +self.nested_struct.write_xdr(w)?; +self.nested_union.write_xdr(w)?; + Ok(()) + }) + } + } + + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") + )] + pub enum TypeVariant { + Uint512, +Uint513, +Uint514, +Str, +Str2, +Hash, +Hashes1, +Hashes2, +Hashes3, +OptHash1, +OptHash2, +Int1, +Int2, +Int3, +Int4, +MyStruct, +LotsOfMyStructs, +HasStuff, +Color, +Nester, +NesterNestedEnum, +NesterNestedStruct, +NesterNestedUnion, + } + + impl TypeVariant { + pub const VARIANTS: [TypeVariant; 23] = [ TypeVariant::Uint512, +TypeVariant::Uint513, +TypeVariant::Uint514, +TypeVariant::Str, +TypeVariant::Str2, +TypeVariant::Hash, +TypeVariant::Hashes1, +TypeVariant::Hashes2, +TypeVariant::Hashes3, +TypeVariant::OptHash1, +TypeVariant::OptHash2, +TypeVariant::Int1, +TypeVariant::Int2, +TypeVariant::Int3, +TypeVariant::Int4, +TypeVariant::MyStruct, +TypeVariant::LotsOfMyStructs, +TypeVariant::HasStuff, +TypeVariant::Color, +TypeVariant::Nester, +TypeVariant::NesterNestedEnum, +TypeVariant::NesterNestedStruct, +TypeVariant::NesterNestedUnion, ]; + pub const VARIANTS_STR: [&'static str; 23] = [ "Uint512", +"Uint513", +"Uint514", +"Str", +"Str2", +"Hash", +"Hashes1", +"Hashes2", +"Hashes3", +"OptHash1", +"OptHash2", +"Int1", +"Int2", +"Int3", +"Int4", +"MyStruct", +"LotsOfMyStructs", +"HasStuff", +"Color", +"Nester", +"NesterNestedEnum", +"NesterNestedStruct", +"NesterNestedUnion", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::Uint512 => "Uint512", +Self::Uint513 => "Uint513", +Self::Uint514 => "Uint514", +Self::Str => "Str", +Self::Str2 => "Str2", +Self::Hash => "Hash", +Self::Hashes1 => "Hashes1", +Self::Hashes2 => "Hashes2", +Self::Hashes3 => "Hashes3", +Self::OptHash1 => "OptHash1", +Self::OptHash2 => "OptHash2", +Self::Int1 => "Int1", +Self::Int2 => "Int2", +Self::Int3 => "Int3", +Self::Int4 => "Int4", +Self::MyStruct => "MyStruct", +Self::LotsOfMyStructs => "LotsOfMyStructs", +Self::HasStuff => "HasStuff", +Self::Color => "Color", +Self::Nester => "Nester", +Self::NesterNestedEnum => "NesterNestedEnum", +Self::NesterNestedStruct => "NesterNestedStruct", +Self::NesterNestedUnion => "NesterNestedUnion", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 23] { + Self::VARIANTS + } + } + + impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "Uint512" => Ok(Self::Uint512), +"Uint513" => Ok(Self::Uint513), +"Uint514" => Ok(Self::Uint514), +"Str" => Ok(Self::Str), +"Str2" => Ok(Self::Str2), +"Hash" => Ok(Self::Hash), +"Hashes1" => Ok(Self::Hashes1), +"Hashes2" => Ok(Self::Hashes2), +"Hashes3" => Ok(Self::Hashes3), +"OptHash1" => Ok(Self::OptHash1), +"OptHash2" => Ok(Self::OptHash2), +"Int1" => Ok(Self::Int1), +"Int2" => Ok(Self::Int2), +"Int3" => Ok(Self::Int3), +"Int4" => Ok(Self::Int4), +"MyStruct" => Ok(Self::MyStruct), +"LotsOfMyStructs" => Ok(Self::LotsOfMyStructs), +"HasStuff" => Ok(Self::HasStuff), +"Color" => Ok(Self::Color), +"Nester" => Ok(Self::Nester), +"NesterNestedEnum" => Ok(Self::NesterNestedEnum), +"NesterNestedStruct" => Ok(Self::NesterNestedStruct), +"NesterNestedUnion" => Ok(Self::NesterNestedUnion), + _ => Err(Error::Invalid), + } + } + } + + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), + )] + pub enum Type { + Uint512(Box), +Uint513(Box), +Uint514(Box), +Str(Box), +Str2(Box), +Hash(Box), +Hashes1(Box), +Hashes2(Box), +Hashes3(Box), +OptHash1(Box), +OptHash2(Box), +Int1(Box), +Int2(Box), +Int3(Box), +Int4(Box), +MyStruct(Box), +LotsOfMyStructs(Box), +HasStuff(Box), +Color(Box), +Nester(Box), +NesterNestedEnum(Box), +NesterNestedStruct(Box), +NesterNestedUnion(Box), + } + + impl Type { + pub const VARIANTS: [TypeVariant; 23] = [ TypeVariant::Uint512, +TypeVariant::Uint513, +TypeVariant::Uint514, +TypeVariant::Str, +TypeVariant::Str2, +TypeVariant::Hash, +TypeVariant::Hashes1, +TypeVariant::Hashes2, +TypeVariant::Hashes3, +TypeVariant::OptHash1, +TypeVariant::OptHash2, +TypeVariant::Int1, +TypeVariant::Int2, +TypeVariant::Int3, +TypeVariant::Int4, +TypeVariant::MyStruct, +TypeVariant::LotsOfMyStructs, +TypeVariant::HasStuff, +TypeVariant::Color, +TypeVariant::Nester, +TypeVariant::NesterNestedEnum, +TypeVariant::NesterNestedStruct, +TypeVariant::NesterNestedUnion, ]; + pub const VARIANTS_STR: [&'static str; 23] = [ "Uint512", +"Uint513", +"Uint514", +"Str", +"Str2", +"Hash", +"Hashes1", +"Hashes2", +"Hashes3", +"OptHash1", +"OptHash2", +"Int1", +"Int2", +"Int3", +"Int4", +"MyStruct", +"LotsOfMyStructs", +"HasStuff", +"Color", +"Nester", +"NesterNestedEnum", +"NesterNestedStruct", +"NesterNestedUnion", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::Uint512 => r.with_limited_depth(|r| Ok(Self::Uint512(Box::new(Uint512::read_xdr(r)?)))), +TypeVariant::Uint513 => r.with_limited_depth(|r| Ok(Self::Uint513(Box::new(Uint513::read_xdr(r)?)))), +TypeVariant::Uint514 => r.with_limited_depth(|r| Ok(Self::Uint514(Box::new(Uint514::read_xdr(r)?)))), +TypeVariant::Str => r.with_limited_depth(|r| Ok(Self::Str(Box::new(Str::read_xdr(r)?)))), +TypeVariant::Str2 => r.with_limited_depth(|r| Ok(Self::Str2(Box::new(Str2::read_xdr(r)?)))), +TypeVariant::Hash => r.with_limited_depth(|r| Ok(Self::Hash(Box::new(Hash::read_xdr(r)?)))), +TypeVariant::Hashes1 => r.with_limited_depth(|r| Ok(Self::Hashes1(Box::new(Hashes1::read_xdr(r)?)))), +TypeVariant::Hashes2 => r.with_limited_depth(|r| Ok(Self::Hashes2(Box::new(Hashes2::read_xdr(r)?)))), +TypeVariant::Hashes3 => r.with_limited_depth(|r| Ok(Self::Hashes3(Box::new(Hashes3::read_xdr(r)?)))), +TypeVariant::OptHash1 => r.with_limited_depth(|r| Ok(Self::OptHash1(Box::new(OptHash1::read_xdr(r)?)))), +TypeVariant::OptHash2 => r.with_limited_depth(|r| Ok(Self::OptHash2(Box::new(OptHash2::read_xdr(r)?)))), +TypeVariant::Int1 => r.with_limited_depth(|r| Ok(Self::Int1(Box::new(Int1::read_xdr(r)?)))), +TypeVariant::Int2 => r.with_limited_depth(|r| Ok(Self::Int2(Box::new(Int2::read_xdr(r)?)))), +TypeVariant::Int3 => r.with_limited_depth(|r| Ok(Self::Int3(Box::new(Int3::read_xdr(r)?)))), +TypeVariant::Int4 => r.with_limited_depth(|r| Ok(Self::Int4(Box::new(Int4::read_xdr(r)?)))), +TypeVariant::MyStruct => r.with_limited_depth(|r| Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?)))), +TypeVariant::LotsOfMyStructs => r.with_limited_depth(|r| Ok(Self::LotsOfMyStructs(Box::new(LotsOfMyStructs::read_xdr(r)?)))), +TypeVariant::HasStuff => r.with_limited_depth(|r| Ok(Self::HasStuff(Box::new(HasStuff::read_xdr(r)?)))), +TypeVariant::Color => r.with_limited_depth(|r| Ok(Self::Color(Box::new(Color::read_xdr(r)?)))), +TypeVariant::Nester => r.with_limited_depth(|r| Ok(Self::Nester(Box::new(Nester::read_xdr(r)?)))), +TypeVariant::NesterNestedEnum => r.with_limited_depth(|r| Ok(Self::NesterNestedEnum(Box::new(NesterNestedEnum::read_xdr(r)?)))), +TypeVariant::NesterNestedStruct => r.with_limited_depth(|r| Ok(Self::NesterNestedStruct(Box::new(NesterNestedStruct::read_xdr(r)?)))), +TypeVariant::NesterNestedUnion => r.with_limited_depth(|r| Ok(Self::NesterNestedUnion(Box::new(NesterNestedUnion::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str(Box::new(t))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str2(Box::new(t))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hash(Box::new(t))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int1(Box::new(t))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int2(Box::new(t))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int3(Box::new(t))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int4(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Nester(Box::new(t))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint512(Box::new(t.0))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint513(Box::new(t.0))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Uint514(Box::new(t.0))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str(Box::new(t.0))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Str2(Box::new(t.0))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hash(Box::new(t.0))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes1(Box::new(t.0))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes2(Box::new(t.0))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Hashes3(Box::new(t.0))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash1(Box::new(t.0))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::OptHash2(Box::new(t.0))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int1(Box::new(t.0))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int2(Box::new(t.0))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int3(Box::new(t.0))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Int4(Box::new(t.0))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t.0))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::HasStuff(Box::new(t.0))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Nester(Box::new(t.0))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t.0))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t.0))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Str(Box::new(t))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Str2(Box::new(t))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hash(Box::new(t))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int1(Box::new(t))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int2(Box::new(t))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int3(Box::new(t))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Int4(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Nester(Box::new(t))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::Uint512 => Ok(Self::Uint512(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Uint513 => Ok(Self::Uint513(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Uint514 => Ok(Self::Uint514(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Str => Ok(Self::Str(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Str2 => Ok(Self::Str2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hash => Ok(Self::Hash(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hashes1 => Ok(Self::Hashes1(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hashes2 => Ok(Self::Hashes2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Hashes3 => Ok(Self::Hashes3(Box::new(serde_json::from_reader(r)?))), +TypeVariant::OptHash1 => Ok(Self::OptHash1(Box::new(serde_json::from_reader(r)?))), +TypeVariant::OptHash2 => Ok(Self::OptHash2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int1 => Ok(Self::Int1(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int2 => Ok(Self::Int2(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int3 => Ok(Self::Int3(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Int4 => Ok(Self::Int4(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyStruct => Ok(Self::MyStruct(Box::new(serde_json::from_reader(r)?))), +TypeVariant::LotsOfMyStructs => Ok(Self::LotsOfMyStructs(Box::new(serde_json::from_reader(r)?))), +TypeVariant::HasStuff => Ok(Self::HasStuff(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Color => Ok(Self::Color(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Nester => Ok(Self::Nester(Box::new(serde_json::from_reader(r)?))), +TypeVariant::NesterNestedEnum => Ok(Self::NesterNestedEnum(Box::new(serde_json::from_reader(r)?))), +TypeVariant::NesterNestedStruct => Ok(Self::NesterNestedStruct(Box::new(serde_json::from_reader(r)?))), +TypeVariant::NesterNestedUnion => Ok(Self::NesterNestedUnion(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::Uint512(ref v) => v.as_ref(), +Self::Uint513(ref v) => v.as_ref(), +Self::Uint514(ref v) => v.as_ref(), +Self::Str(ref v) => v.as_ref(), +Self::Str2(ref v) => v.as_ref(), +Self::Hash(ref v) => v.as_ref(), +Self::Hashes1(ref v) => v.as_ref(), +Self::Hashes2(ref v) => v.as_ref(), +Self::Hashes3(ref v) => v.as_ref(), +Self::OptHash1(ref v) => v.as_ref(), +Self::OptHash2(ref v) => v.as_ref(), +Self::Int1(ref v) => v.as_ref(), +Self::Int2(ref v) => v.as_ref(), +Self::Int3(ref v) => v.as_ref(), +Self::Int4(ref v) => v.as_ref(), +Self::MyStruct(ref v) => v.as_ref(), +Self::LotsOfMyStructs(ref v) => v.as_ref(), +Self::HasStuff(ref v) => v.as_ref(), +Self::Color(ref v) => v.as_ref(), +Self::Nester(ref v) => v.as_ref(), +Self::NesterNestedEnum(ref v) => v.as_ref(), +Self::NesterNestedStruct(ref v) => v.as_ref(), +Self::NesterNestedUnion(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::Uint512(_) => "Uint512", +Self::Uint513(_) => "Uint513", +Self::Uint514(_) => "Uint514", +Self::Str(_) => "Str", +Self::Str2(_) => "Str2", +Self::Hash(_) => "Hash", +Self::Hashes1(_) => "Hashes1", +Self::Hashes2(_) => "Hashes2", +Self::Hashes3(_) => "Hashes3", +Self::OptHash1(_) => "OptHash1", +Self::OptHash2(_) => "OptHash2", +Self::Int1(_) => "Int1", +Self::Int2(_) => "Int2", +Self::Int3(_) => "Int3", +Self::Int4(_) => "Int4", +Self::MyStruct(_) => "MyStruct", +Self::LotsOfMyStructs(_) => "LotsOfMyStructs", +Self::HasStuff(_) => "HasStuff", +Self::Color(_) => "Color", +Self::Nester(_) => "Nester", +Self::NesterNestedEnum(_) => "NesterNestedEnum", +Self::NesterNestedStruct(_) => "NesterNestedStruct", +Self::NesterNestedUnion(_) => "NesterNestedUnion", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 23] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::Uint512(_) => TypeVariant::Uint512, +Self::Uint513(_) => TypeVariant::Uint513, +Self::Uint514(_) => TypeVariant::Uint514, +Self::Str(_) => TypeVariant::Str, +Self::Str2(_) => TypeVariant::Str2, +Self::Hash(_) => TypeVariant::Hash, +Self::Hashes1(_) => TypeVariant::Hashes1, +Self::Hashes2(_) => TypeVariant::Hashes2, +Self::Hashes3(_) => TypeVariant::Hashes3, +Self::OptHash1(_) => TypeVariant::OptHash1, +Self::OptHash2(_) => TypeVariant::OptHash2, +Self::Int1(_) => TypeVariant::Int1, +Self::Int2(_) => TypeVariant::Int2, +Self::Int3(_) => TypeVariant::Int3, +Self::Int4(_) => TypeVariant::Int4, +Self::MyStruct(_) => TypeVariant::MyStruct, +Self::LotsOfMyStructs(_) => TypeVariant::LotsOfMyStructs, +Self::HasStuff(_) => TypeVariant::HasStuff, +Self::Color(_) => TypeVariant::Color, +Self::Nester(_) => TypeVariant::Nester, +Self::NesterNestedEnum(_) => TypeVariant::NesterNestedEnum, +Self::NesterNestedStruct(_) => TypeVariant::NesterNestedStruct, +Self::NesterNestedUnion(_) => TypeVariant::NesterNestedUnion, + } + } + } + + impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::Uint512(v) => v.write_xdr(w), +Self::Uint513(v) => v.write_xdr(w), +Self::Uint514(v) => v.write_xdr(w), +Self::Str(v) => v.write_xdr(w), +Self::Str2(v) => v.write_xdr(w), +Self::Hash(v) => v.write_xdr(w), +Self::Hashes1(v) => v.write_xdr(w), +Self::Hashes2(v) => v.write_xdr(w), +Self::Hashes3(v) => v.write_xdr(w), +Self::OptHash1(v) => v.write_xdr(w), +Self::OptHash2(v) => v.write_xdr(w), +Self::Int1(v) => v.write_xdr(w), +Self::Int2(v) => v.write_xdr(w), +Self::Int3(v) => v.write_xdr(w), +Self::Int4(v) => v.write_xdr(w), +Self::MyStruct(v) => v.write_xdr(w), +Self::LotsOfMyStructs(v) => v.write_xdr(w), +Self::HasStuff(v) => v.write_xdr(w), +Self::Color(v) => v.write_xdr(w), +Self::Nester(v) => v.write_xdr(w), +Self::NesterNestedEnum(v) => v.write_xdr(w), +Self::NesterNestedStruct(v) => v.write_xdr(w), +Self::NesterNestedUnion(v) => v.write_xdr(w), + } + } + } diff --git a/spec/output/generator_spec_rust_custom_str_impls/union.x/MyXDR.rs b/spec/output/generator_spec_rust_custom_str_impls/union.x/MyXDR.rs new file mode 100644 index 000000000..a6b641f07 --- /dev/null +++ b/spec/output/generator_spec_rust_custom_str_impls/union.x/MyXDR.rs @@ -0,0 +1,3365 @@ +// Module is generated from: +// spec/fixtures/generator/union.x + +#![allow(clippy::missing_errors_doc, clippy::unreadable_literal)] + +/// `XDR_FILES_SHA256` is a list of pairs of source files and their SHA256 hashes. +pub const XDR_FILES_SHA256: [(&str, &str); 1] = [ + ("spec/fixtures/generator/union.x", "c251258d967223b341ebcf2d5bb0718e9a039b46232cb743865d9acd0c4bbe41") +]; + +use core::{array::TryFromSliceError, fmt, fmt::Debug, marker::Sized, ops::Deref, slice}; + +#[cfg(feature = "std")] +use core::marker::PhantomData; + +// When feature alloc is turned off use static lifetime Box and Vec types. +#[cfg(not(feature = "alloc"))] +mod noalloc { + pub mod boxed { + pub type Box = &'static T; + } + pub mod vec { + pub type Vec = &'static [T]; + } +} +#[cfg(not(feature = "alloc"))] +use noalloc::{boxed::Box, vec::Vec}; + +// When feature std is turned off, but feature alloc is turned on import the +// alloc crate and use its Box and Vec types. +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{ + borrow::ToOwned, + boxed::Box, + string::{FromUtf8Error, String}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::string::FromUtf8Error; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +// TODO: Add support for read/write xdr fns when std not available. + +#[cfg(feature = "std")] +use std::{ + error, io, + io::{BufRead, BufReader, Cursor, Read, Write}, +}; + +/// Error contains all errors returned by functions in this crate. It can be +/// compared via `PartialEq`, however any contained IO errors will only be +/// compared on their `ErrorKind`. +#[derive(Debug)] +pub enum Error { + Invalid, + Unsupported, + LengthExceedsMax, + LengthMismatch, + NonZeroPadding, + Utf8Error(core::str::Utf8Error), + #[cfg(feature = "alloc")] + InvalidHex, + #[cfg(feature = "std")] + Io(io::Error), + DepthLimitExceeded, + #[cfg(feature = "serde_json")] + Json(serde_json::Error), + LengthLimitExceeded, +} + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Utf8Error(l), Self::Utf8Error(r)) => l == r, + // IO errors cannot be compared, but in the absence of any more + // meaningful way to compare the errors we compare the kind of error + // and ignore the embedded source error or OS error. The main use + // case for comparing errors outputted by the XDR library is for + // error case testing, and a lack of the ability to compare has a + // detrimental affect on failure testing, so this is a tradeoff. + #[cfg(feature = "std")] + (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + #[must_use] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + #[cfg(feature = "serde_json")] + Self::Json(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Invalid => write!(f, "xdr value invalid"), + Error::Unsupported => write!(f, "xdr value unsupported"), + Error::LengthExceedsMax => write!(f, "xdr value max length exceeded"), + Error::LengthMismatch => write!(f, "xdr value length does not match"), + Error::NonZeroPadding => write!(f, "xdr padding contains non-zero bytes"), + Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "alloc")] + Error::InvalidHex => write!(f, "hex invalid"), + #[cfg(feature = "std")] + Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), + #[cfg(feature = "serde_json")] + Error::Json(e) => write!(f, "{e}"), + Error::LengthLimitExceeded => write!(f, "length limit exceeded"), + } + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Error { + Error::LengthMismatch + } +} + +impl From for Error { + #[must_use] + fn from(e: core::str::Utf8Error) -> Self { + Error::Utf8Error(e) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + #[must_use] + fn from(e: FromUtf8Error) -> Self { + Error::Utf8Error(e.utf8_error()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + #[must_use] + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +#[cfg(feature = "serde_json")] +impl From for Error { + #[must_use] + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for () { + fn from(_: Error) {} +} + +#[allow(dead_code)] +type Result = core::result::Result; + +/// Name defines types that assign a static name to their value, such as the +/// name given to an identifier in an XDR enum, or the name given to the case in +/// a union. +pub trait Name { + fn name(&self) -> &'static str; +} + +/// Discriminant defines types that may contain a one-of value determined +/// according to the discriminant, and exposes the value of the discriminant for +/// that type, such as in an XDR union. +pub trait Discriminant { + fn discriminant(&self) -> D; +} + +/// Iter defines types that have variants that can be iterated. +pub trait Variants { + fn variants() -> slice::Iter<'static, V> + where + V: Sized; +} + +// Enum defines a type that is represented as an XDR enumeration when encoded. +pub trait Enum: Name + Variants + Sized {} + +// Union defines a type that is represented as an XDR union when encoded. +pub trait Union: Name + Discriminant + Variants +where + D: Sized, +{ +} + +/// `Limits` contains the limits that a limited reader or writer will be +/// constrained to. +#[cfg(feature = "std")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Limits { + /// Defines the maximum depth for recursive calls in `Read/WriteXdr` to + /// prevent stack overflow. + /// + /// The depth limit is akin to limiting stack depth. Its purpose is to + /// prevent the program from hitting the maximum stack size allowed by Rust, + /// which would result in an unrecoverable `SIGABRT`. For more information + /// about Rust's stack size limit, refer to the [Rust + /// documentation](https://doc.rust-lang.org/std/thread/#stack-size). + pub depth: u32, + + /// Defines the maximum number of bytes that will be read or written. + pub len: usize, +} + +#[cfg(feature = "std")] +impl Limits { + #[must_use] + pub fn none() -> Self { + Self { + depth: u32::MAX, + len: usize::MAX, + } + } + + #[must_use] + pub fn depth(depth: u32) -> Self { + Limits { + depth, + ..Limits::none() + } + } + + #[must_use] + pub fn len(len: usize) -> Self { + Limits { + len, + ..Limits::none() + } + } +} + +/// `Limited` wraps an object and provides functions for enforcing limits. +/// +/// Intended for use with readers and writers and limiting their reads and +/// writes. +#[cfg(feature = "std")] +pub struct Limited { + pub inner: L, + pub(crate) limits: Limits, +} + +#[cfg(feature = "std")] +impl Limited { + /// Constructs a new `Limited`. + /// + /// - `inner`: The value being limited. + /// - `limits`: The limits to enforce. + pub fn new(inner: L, limits: Limits) -> Self { + Limited { inner, limits } + } + + /// Consume the given length from the internal remaining length limit. + /// + /// ### Errors + /// + /// If the length would consume more length than the remaining length limit + /// allows. + pub(crate) fn consume_len(&mut self, len: usize) -> Result<()> { + if let Some(len) = self.limits.len.checked_sub(len) { + self.limits.len = len; + Ok(()) + } else { + Err(Error::LengthLimitExceeded) + } + } + + /// Consumes a single depth for the duration of the given function. + /// + /// ### Errors + /// + /// If the depth limit is already exhausted. + pub(crate) fn with_limited_depth(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + if let Some(depth) = self.limits.depth.checked_sub(1) { + self.limits.depth = depth; + let res = f(self); + self.limits.depth = self.limits.depth.saturating_add(1); + res + } else { + Err(Error::DepthLimitExceeded) + } + } +} + +#[cfg(feature = "std")] +impl Read for Limited { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +#[cfg(feature = "std")] +impl BufRead for Limited { + /// Forwards the read operation to the wrapped object. + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.inner.fill_buf() + } + + /// Forwards the read operation to the wrapped object. + fn consume(&mut self, amt: usize) { + self.inner.consume(amt); + } +} + +#[cfg(feature = "std")] +impl Write for Limited { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "std")] +pub struct ReadXdrIter { + reader: Limited>, + _s: PhantomData, +} + +#[cfg(feature = "std")] +impl ReadXdrIter { + fn new(r: R, limits: Limits) -> Self { + Self { + reader: Limited { + inner: BufReader::new(r), + limits, + }, + _s: PhantomData, + } + } +} + +#[cfg(feature = "std")] +impl Iterator for ReadXdrIter { + type Item = Result; + + // Next reads the internal reader and XDR decodes it into the Self type. If + // the EOF is reached without reading any new bytes `None` is returned. If + // EOF is reached after reading some bytes a truncated entry is assumed an + // an `Error::Io` containing an `UnexpectedEof`. If any other IO error + // occurs it is returned. Iteration of this iterator stops naturally when + // `None` is returned, but not when a `Some(Err(...))` is returned. The + // caller is responsible for checking each Result. + fn next(&mut self) -> Option { + // Try to fill the buffer to see if the EOF has been reached or not. + // This happens to effectively peek to see if the stream has finished + // and there are no more items. It is necessary to do this because the + // xdr types in this crate heavily use the `std::io::Read::read_exact` + // method that doesn't distinguish between an EOF at the beginning of a + // read and an EOF after a partial fill of a read_exact. + match self.reader.fill_buf() { + // If the reader has no more data and is unable to fill any new data + // into its internal buf, then the EOF has been reached. + Ok([]) => return None, + // If an error occurs filling the buffer, treat that as an error and stop. + Err(e) => return Some(Err(Error::Io(e))), + // If there is data in the buf available for reading, continue. + Ok([..]) => (), + }; + // Read the buf into the type. + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { + Ok(s) => Some(Ok(s)), + Err(e) => Some(Err(e)), + } + } +} + +pub trait ReadXdr +where + Self: Sized, +{ + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the + /// read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result; + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_to_end(r: &mut Limited) -> Result { + let s = Self::read_xdr(r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn read_xdr_base64_to_end(r: &mut Limited) -> Result { + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.limits.clone(), + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } + + /// Read the XDR and construct the type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type. Any residual bytes remain in the read implementation. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + /// + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes + /// in the read implementation to be consumed by the read. + #[cfg(feature = "std")] + fn read_xdr_into(&mut self, r: &mut Limited) -> Result<()> { + *self = Self::read_xdr(r)?; + Ok(()) + } + + /// Read the XDR into the existing value, and consider it an error if the + /// read does not completely consume the read implementation. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_into_to_end(&mut self, r: &mut Limited) -> Result<()> { + Self::read_xdr_into(self, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(()) + } else { + Err(Error::Invalid) + } + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + /// + /// Read bytes from the given read implementation, decoding the bytes as + /// XDR, and construct the type implementing this interface from those + /// bytes. + /// + /// Just enough bytes are read from the read implementation to construct the + /// type, and then confirm that no further bytes remain. To confirm no + /// further bytes remain additional bytes are attempted to be read from the + /// read implementation. If it is possible to read any residual bytes from + /// the read implementation an error is returned. The read implementation + /// may not be exhaustively read if there are residual bytes, and it is + /// considered undefined how many residual bytes or how much of the residual + /// buffer are consumed in this case. + /// + /// All implementations should continue if the read implementation returns + /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). + #[cfg(feature = "std")] + fn read_xdr_iter(r: &mut Limited) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.limits.clone()) + } + + /// Create an iterator that reads the read implementation as a stream of + /// values that are read into the implementing type. + #[cfg(feature = "base64")] + fn read_xdr_base64_iter( + r: &mut Limited, + ) -> ReadXdrIter, Self> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.limits.clone()) + } + + /// Construct the type from the XDR bytes. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(&mut cursor)?; + Ok(t) + } + + /// Construct the type from the XDR bytes base64 encoded. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + limits, + ); + let t = Self::read_xdr_to_end(&mut dec)?; + Ok(t) + } +} + +pub trait WriteXdr { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()>; + + #[cfg(feature = "std")] + fn to_xdr(&self, limits: Limits) -> Result> { + let mut cursor = Limited::new(Cursor::new(vec![]), limits); + self.write_xdr(&mut cursor)?; + let bytes = cursor.inner.into_inner(); + Ok(bytes) + } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self, limits: Limits) -> Result { + let mut enc = Limited::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + limits, + ); + self.write_xdr(&mut enc)?; + let b64 = enc.inner.into_inner(); + Ok(b64) + } +} + +/// `Pad_len` returns the number of bytes to pad an XDR value of the given +/// length to make the final serialized size a multiple of 4. +#[cfg(feature = "std")] +fn pad_len(len: usize) -> usize { + (4 - (len % 4)) % 4 +} + +impl ReadXdr for i32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u32 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 4]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u32 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 4] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for i64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for i64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for u64 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + let mut b = [0u8; 8]; + r.with_limited_depth(|r| { + r.consume_len(b.len())?; + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) + } +} + +impl WriteXdr for u64 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + let b: [u8; 8] = self.to_be_bytes(); + w.with_limited_depth(|w| { + w.consume_len(b.len())?; + Ok(w.write_all(&b)?) + }) + } +} + +impl ReadXdr for f32 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f32 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for f64 { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + todo!() + } +} + +impl WriteXdr for f64 { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + todo!() + } +} + +impl ReadXdr for bool { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) + } +} + +impl WriteXdr for bool { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) + } +} + +impl ReadXdr for Option { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), + } + }) + } +} + +impl WriteXdr for Option { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) + } +} + +impl ReadXdr for Box { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) + } +} + +impl WriteXdr for Box { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) + } +} + +impl ReadXdr for () { + #[cfg(feature = "std")] + fn read_xdr(_r: &mut Limited) -> Result { + Ok(()) + } +} + +impl WriteXdr for () { + #[cfg(feature = "std")] + fn write_xdr(&self, _w: &mut Limited) -> Result<()> { + Ok(()) + } +} + +impl ReadXdr for [u8; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + r.consume_len(N)?; + let padding = pad_len(N); + r.consume_len(padding)?; + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) + } +} + +impl WriteXdr for [u8; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + w.consume_len(N)?; + let padding = pad_len(N); + w.consume_len(padding)?; + w.write_all(self)?; + w.write_all(&[0u8; 3][..padding])?; + Ok(()) + }) + } +} + +impl ReadXdr for [T; N] { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) + } +} + +impl WriteXdr for [T; N] { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) + } +} + +// VecM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct VecM(Vec) +where + T: 'static; + +impl Deref for VecM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for VecM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl VecM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl VecM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl VecM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl VecM { + #[must_use] + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.0[0].clone()) + } else { + None + } + } +} + +#[cfg(not(feature = "alloc"))] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.to_option() + } +} + +#[cfg(feature = "alloc")] +impl VecM { + #[must_use] + pub fn into_option(mut self) -> Option { + self.0.drain(..).next() + } +} + +#[cfg(feature = "alloc")] +impl From> for Option { + #[must_use] + fn from(v: VecM) -> Self { + v.into_option() + } +} + +impl TryFrom> for VecM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: VecM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&VecM> for Vec { + #[must_use] + fn from(v: &VecM) -> Self { + v.0.clone() + } +} + +impl AsRef> for VecM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for VecM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T]> for VecM { + type Error = Error; + + fn try_from(v: &[T]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[T]> for VecM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[T] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[T; N]> for VecM { + type Error = Error; + + fn try_from(v: [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [T; N] { + type Error = VecM; + + fn try_from(v: VecM) -> core::result::Result { + let s: [T; N] = v.0.try_into().map_err(|v: Vec| VecM::(v))?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[T; N]> for VecM { + type Error = Error; + + fn try_from(v: &[T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [T; N]> for VecM { + type Error = Error; + + fn try_from(v: &'static [T; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for VecM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for VecM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: VecM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&VecM> for String { + type Error = Error; + + fn try_from(v: &VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for VecM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for VecM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(VecM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { + type Error = Error; + + fn try_from(v: &'a VecM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +impl ReadXdr for VecM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + let mut vec = Vec::new(); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } + + Ok(VecM(vec)) + }) + } +} + +impl WriteXdr for VecM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + for t in &self.0 { + t.write_xdr(w)?; + } + + Ok(()) + }) + } +} + +// BytesM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct BytesM(Vec); + +impl core::fmt::Display for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + for b in v { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl core::fmt::Debug for BytesM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "BytesM(")?; + for b in v { + write!(f, "{b:02x}")?; + } + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for BytesM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into() + } +} + +impl Deref for BytesM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for BytesM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl BytesM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl BytesM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl BytesM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for BytesM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: BytesM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&BytesM> for Vec { + #[must_use] + fn from(v: &BytesM) -> Self { + v.0.clone() + } +} + +impl AsRef> for BytesM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for BytesM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for BytesM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = BytesM; + + fn try_from(v: BytesM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(BytesM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for BytesM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for BytesM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for BytesM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: BytesM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&BytesM> for String { + type Error = Error; + + fn try_from(v: &BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for BytesM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for BytesM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(BytesM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { + type Error = Error; + + fn try_from(v: &'a BytesM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for BytesM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(BytesM(vec)) + }) + } +} + +impl WriteXdr for BytesM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + + Ok(()) + }) + } +} + +// StringM ------------------------------------------------------------------------ + +#[cfg(feature = "alloc")] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr) +)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +#[cfg(not(feature = "alloc"))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct StringM(Vec); + +/// `write_utf8_lossy` is a modified copy of the Rust stdlib docs examples here: +/// +fn write_utf8_lossy(f: &mut impl core::fmt::Write, mut input: &[u8]) -> core::fmt::Result { + loop { + match core::str::from_utf8(input) { + Ok(valid) => { + write!(f, "{valid}")?; + break; + } + Err(error) => { + let (valid, after_valid) = input.split_at(error.valid_up_to()); + write!(f, "{}", core::str::from_utf8(valid).unwrap())?; + write!(f, "\u{FFFD}")?; + + if let Some(invalid_sequence_length) = error.error_len() { + input = &after_valid[invalid_sequence_length..]; + } else { + break; + } + } + } + } + Ok(()) +} + +impl core::fmt::Display for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write_utf8_lossy(f, v)?; + Ok(()) + } +} + +impl core::fmt::Debug for StringM { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "alloc")] + let v = &self.0; + #[cfg(not(feature = "alloc"))] + let v = self.0; + write!(f, "StringM(")?; + write_utf8_lossy(f, v)?; + write!(f, ")")?; + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl core::str::FromStr for StringM { + type Err = Error; + fn from_str(s: &str) -> core::result::Result { + s.try_into() + } +} + +impl Deref for StringM { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for StringM { + fn default() -> Self { + Self(Vec::default()) + } +} + +impl StringM { + pub const MAX_LEN: usize = { MAX as usize }; + + #[must_use] + #[allow(clippy::unused_self)] + pub fn max_len(&self) -> usize { + Self::MAX_LEN + } + + #[must_use] + pub fn as_vec(&self) -> &Vec { + self.as_ref() + } +} + +impl StringM { + #[must_use] + #[cfg(feature = "alloc")] + pub fn to_vec(&self) -> Vec { + self.into() + } + + #[must_use] + pub fn into_vec(self) -> Vec { + self.into() + } +} + +impl StringM { + #[cfg(feature = "alloc")] + pub fn to_string(&self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + pub fn into_string(self) -> Result { + self.try_into() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn to_string_lossy(&self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } + + #[cfg(feature = "alloc")] + #[must_use] + pub fn into_string_lossy(self) -> String { + String::from_utf8_lossy(&self.0).into_owned() + } +} + +impl TryFrom> for StringM { + type Error = Error; + + fn try_from(v: Vec) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl From> for Vec { + #[must_use] + fn from(v: StringM) -> Self { + v.0 + } +} + +#[cfg(feature = "alloc")] +impl From<&StringM> for Vec { + #[must_use] + fn from(v: &StringM) -> Self { + v.0.clone() + } +} + +impl AsRef> for StringM { + #[must_use] + fn as_ref(&self) -> &Vec { + &self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&Vec> for StringM { + type Error = Error; + + fn try_from(v: &Vec) -> Result { + v.as_slice().try_into() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8]> for StringM { + type Error = Error; + + fn try_from(v: &[u8]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl AsRef<[u8]> for StringM { + #[cfg(feature = "alloc")] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } + #[cfg(not(feature = "alloc"))] + #[must_use] + fn as_ref(&self) -> &[u8] { + self.0 + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for [u8; N] { + type Error = StringM; + + fn try_from(v: StringM) -> core::result::Result { + let s: [u8; N] = v.0.try_into().map_err(StringM::)?; + Ok(s) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&[u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &[u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static [u8; N]> for StringM { + type Error = Error; + + fn try_from(v: &'static [u8; N]) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v)) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&String> for StringM { + type Error = Error; + + fn try_from(v: &String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes().to_vec())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom for StringM { + type Error = Error; + + fn try_from(v: String) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for String { + type Error = Error; + + fn try_from(v: StringM) -> Result { + Ok(String::from_utf8(v.0)?) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&StringM> for String { + type Error = Error; + + fn try_from(v: &StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?.to_owned()) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&str> for StringM { + type Error = Error; + + fn try_from(v: &str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.into())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for StringM { + type Error = Error; + + fn try_from(v: &'static str) -> Result { + let len: u32 = v.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + if len <= MAX { + Ok(StringM(v.as_bytes())) + } else { + Err(Error::LengthExceedsMax) + } + } +} + +impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { + type Error = Error; + + fn try_from(v: &'a StringM) -> Result { + Ok(core::str::from_utf8(v.as_ref())?) + } +} + +impl ReadXdr for StringM { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } + + r.consume_len(len as usize)?; + let padding = pad_len(len as usize); + r.consume_len(padding)?; + + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; + + let pad = &mut [0u8; 3][..padding]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + + Ok(StringM(vec)) + }) + } +} + +impl WriteXdr for StringM { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; + + w.consume_len(self.len())?; + let padding = pad_len(self.len()); + w.consume_len(padding)?; + + w.write_all(&self.0)?; + + w.write_all(&[0u8; 3][..padding])?; + + Ok(()) + }) + } +} + +// Frame ------------------------------------------------------------------------ + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct Frame(pub T) +where + T: ReadXdr; + +impl ReadXdr for Frame +where + T: ReadXdr, +{ + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + // Read the frame header value that contains 1 flag-bit and a 33-bit length. + // - The 1 flag bit is 0 when there are more frames for the same record. + // - The 31-bit length is the length of the bytes within the frame that + // follow the frame header. + let header = u32::read_xdr(r)?; + // TODO: Use the length and cap the length we'll read from `r`. + let last_record = header >> 31 == 1; + if last_record { + // Read the record in the frame. + Ok(Self(T::read_xdr(r)?)) + } else { + // TODO: Support reading those additional frames for the same + // record. + Err(Error::Unsupported) + } + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use std::io::Cursor; + + use super::*; + + #[test] + pub fn vec_u8_read_without_padding() { + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_read_with_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v.to_vec(), vec![2]); + } + + #[test] + pub fn vec_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn vec_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = VecM::::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn vec_u8_write_without_padding() { + let mut buf = vec![]; + let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); + + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); + } + + #[test] + pub fn vec_u8_write_with_padding() { + let mut buf = vec![]; + let v: VecM = vec![2].try_into().unwrap(); + v.write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); + } + + #[test] + pub fn arr_u8_read_without_padding() { + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_read_with_padding() { + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())).unwrap(); + assert_eq!(v, [2]); + } + + #[test] + pub fn arr_u8_read_with_insufficient_padding() { + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::Io(_)) => (), + _ => panic!("expected IO error got {res:?}"), + } + } + + #[test] + pub fn arr_u8_read_with_non_zero_padding() { + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut Limited::new(buf, Limits::none())); + match res { + Err(Error::NonZeroPadding) => (), + _ => panic!("expected NonZeroPadding got {res:?}"), + } + } + + #[test] + pub fn arr_u8_write_without_padding() { + let mut buf = vec![]; + [2u8, 2, 2, 2] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 2, 2, 2]); + } + + #[test] + pub fn arr_u8_write_with_padding() { + let mut buf = vec![]; + [2u8] + .write_xdr(&mut Limited::new(Cursor::new(&mut buf), Limits::none())) + .unwrap(); + assert_eq!(buf, vec![2, 0, 0, 0]); + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use super::*; + + #[test] + fn into_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.into_option(), None); + } + + #[test] + fn into_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.into_option(), Some(1)); + } + + #[test] + fn to_option_none() { + let v: VecM = vec![].try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = vec![1].try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(4)); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::depth(4)); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), Limits::depth(3)); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_limits = Limits::depth(3); + let write_limits = Limits::depth(5); + let a: Option>> = Some(Some(Some(5))); + let mut buf = Limited::new(Vec::new(), write_limits); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = Limited::new(Cursor::new(buf.inner.as_slice()), read_limits); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn length_limited_read_write_i32() { + // Exact limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: i32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u32() { + // Exact limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: u32 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u32; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_i64() { + // Exact limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: i64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123i64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_u64() { + // Exact limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: u64 = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = 123u64; + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bool() { + // Exact limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: bool = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = true; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + ::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_option() { + // Exact limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: Option = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = Some(true); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_u8() { + // Exact limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(4)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(5)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(5)); + let v_back: [u8; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(3)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [1u8, 2, 3]; + let mut buf = Limited::new(Vec::new(), Limits::len(4)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(3)); + assert_eq!( + <[u8; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_array_type() { + // Exact limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(12)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(13)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(13)); + let v_back: [bool; 3] = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(11)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = [true, false, true]; + let mut buf = Limited::new(Vec::new(), Limits::len(12)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(11)); + assert_eq!( + <[bool; 3] as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_vec() { + // Exact limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(16)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(17)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(17)); + let v_back: VecM = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(15)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = VecM::::try_from([1i32, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(16)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(15)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_bytes() { + // Exact limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: BytesM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = BytesM::<3>::try_from([1u8, 2, 3]).unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } + + #[test] + fn length_limited_read_write_string() { + // Exact limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(8)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 0); + assert_eq!(v, v_back); + + // Over limit, success + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(9)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 1); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(9)); + let v_back: StringM<3> = ReadXdr::read_xdr(&mut lr).unwrap(); + assert_eq!(buf.limits.len, 1); + assert_eq!(v, v_back); + + // Write under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(7)); + assert_eq!(v.write_xdr(&mut buf), Err(Error::LengthLimitExceeded)); + + // Read under limit, failure + let v = StringM::<3>::try_from("123").unwrap(); + let mut buf = Limited::new(Vec::new(), Limits::len(8)); + v.write_xdr(&mut buf).unwrap(); + assert_eq!(buf.limits.len, 0); + let mut lr = Limited::new(Cursor::new(buf.inner.as_slice()), Limits::len(7)); + assert_eq!( + as ReadXdr>::read_xdr(&mut lr), + Err(Error::LengthLimitExceeded) + ); + } +} + +#[cfg(all(test, not(feature = "alloc")))] +mod test { + use super::VecM; + + #[test] + fn to_option_none() { + let v: VecM = (&[]).try_into().unwrap(); + assert_eq!(v.to_option(), None); + } + + #[test] + fn to_option_some() { + let v: VecM<_, 1> = (&[1]).try_into().unwrap(); + assert_eq!(v.to_option(), Some(1)); + } +} + +// SError is an XDR Typedef defines as: +// +// typedef int Error; +// +pub type SError = i32; + +// Multi is an XDR Typedef defines as: +// +// typedef int Multi; +// +pub type Multi = i32; + +// UnionKey is an XDR Enum defines as: +// +// enum UnionKey { +// ERROR, +// MULTI +// }; +// +// enum +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +#[repr(i32)] +pub enum UnionKey { + Error = 0, + Multi = 1, +} + + impl UnionKey { + pub const VARIANTS: [UnionKey; 2] = [ UnionKey::Error, +UnionKey::Multi, ]; + pub const VARIANTS_STR: [&'static str; 2] = [ "Error", +"Multi", ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::Error => "Error", +Self::Multi => "Multi", + } + } + + #[must_use] + pub const fn variants() -> [UnionKey; 2] { + Self::VARIANTS + } + } + + impl Name for UnionKey { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for UnionKey { + fn variants() -> slice::Iter<'static, UnionKey> { + Self::VARIANTS.iter() + } + } + + impl Enum for UnionKey {} + + impl fmt::Display for UnionKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl TryFrom for UnionKey { + type Error = Error; + + fn try_from(i: i32) -> Result { + let e = match i { + 0 => UnionKey::Error, +1 => UnionKey::Multi, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(e) + } + } + + impl From for i32 { + #[must_use] + fn from(e: UnionKey) -> Self { + e as Self + } + } + + impl ReadXdr for UnionKey { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) + } + } + + impl WriteXdr for UnionKey { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) + } + } + +// MyUnion is an XDR Union defines as: +// +// union MyUnion switch (UnionKey type) +// { +// case ERROR: +// Error error; +// case MULTI: +// Multi things<>; +// +// +// }; +// +// union with discriminant UnionKey +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))] +#[allow(clippy::large_enum_variant)] +pub enum MyUnion { + Error(i32), + Multi(VecM::), +} + + impl MyUnion { + pub const VARIANTS: [UnionKey; 2] = [ + UnionKey::Error, +UnionKey::Multi, + ]; + pub const VARIANTS_STR: [&'static str; 2] = [ + "Error", +"Multi", + ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::Error(_) => "Error", +Self::Multi(_) => "Multi", + } + } + + #[must_use] + pub const fn discriminant(&self) -> UnionKey { + #[allow(clippy::match_same_arms)] + match self { + Self::Error(_) => UnionKey::Error, +Self::Multi(_) => UnionKey::Multi, + } + } + + #[must_use] + pub const fn variants() -> [UnionKey; 2] { + Self::VARIANTS + } + } + + impl Name for MyUnion { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Discriminant for MyUnion { + #[must_use] + fn discriminant(&self) -> UnionKey { + Self::discriminant(self) + } + } + + impl Variants for MyUnion { + fn variants() -> slice::Iter<'static, UnionKey> { + Self::VARIANTS.iter() + } + } + + impl Union for MyUnion {} + + impl ReadXdr for MyUnion { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let dv: UnionKey = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + UnionKey::Error => Self::Error(i32::read_xdr(r)?), +UnionKey::Multi => Self::Multi(VecM::::read_xdr(r)?), + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) + } + } + + impl WriteXdr for MyUnion { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::Error(v) => v.write_xdr(w)?, +Self::Multi(v) => v.write_xdr(w)?, + }; + Ok(()) + }) + } + } + +// IntUnion is an XDR Union defines as: +// +// union IntUnion switch (int type) +// { +// case 0: +// Error error; +// case 1: +// Multi things<>; +// +// }; +// +// union with discriminant i32 +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[allow(clippy::large_enum_variant)] +pub enum IntUnion { + V0(i32), + V1(VecM::), +} + + impl IntUnion { + pub const VARIANTS: [i32; 2] = [ + 0, +1, + ]; + pub const VARIANTS_STR: [&'static str; 2] = [ + "V0", +"V1", + ]; + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::V0(_) => "V0", +Self::V1(_) => "V1", + } + } + + #[must_use] + pub const fn discriminant(&self) -> i32 { + #[allow(clippy::match_same_arms)] + match self { + Self::V0(_) => 0, +Self::V1(_) => 1, + } + } + + #[must_use] + pub const fn variants() -> [i32; 2] { + Self::VARIANTS + } + } + + impl Name for IntUnion { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Discriminant for IntUnion { + #[must_use] + fn discriminant(&self) -> i32 { + Self::discriminant(self) + } + } + + impl Variants for IntUnion { + fn variants() -> slice::Iter<'static, i32> { + Self::VARIANTS.iter() + } + } + + impl Union for IntUnion {} + + impl ReadXdr for IntUnion { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let dv: i32 = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + 0 => Self::V0(i32::read_xdr(r)?), +1 => Self::V1(VecM::::read_xdr(r)?), + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) + } + } + + impl WriteXdr for IntUnion { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::V0(v) => v.write_xdr(w)?, +Self::V1(v) => v.write_xdr(w)?, + }; + Ok(()) + }) + } + } + +// IntUnion2 is an XDR Typedef defines as: +// +// typedef IntUnion IntUnion2; +// +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +#[cfg_attr(all(feature = "serde", feature = "alloc"), derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))] +#[derive(Debug)] +pub struct IntUnion2(pub IntUnion); + +impl From for IntUnion { + #[must_use] + fn from(x: IntUnion2) -> Self { + x.0 + } +} + +impl From for IntUnion2 { + #[must_use] + fn from(x: IntUnion) -> Self { + IntUnion2(x) + } +} + +impl AsRef for IntUnion2 { + #[must_use] + fn as_ref(&self) -> &IntUnion { + &self.0 + } +} + +impl ReadXdr for IntUnion2 { + #[cfg(feature = "std")] + fn read_xdr(r: &mut Limited) -> Result { + r.with_limited_depth(|r| { + let i = IntUnion::read_xdr(r)?; + let v = IntUnion2(i); + Ok(v) + }) + } +} + +impl WriteXdr for IntUnion2 { + #[cfg(feature = "std")] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) + } +} + + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") + )] + pub enum TypeVariant { + SError, +Multi, +UnionKey, +MyUnion, +IntUnion, +IntUnion2, + } + + impl TypeVariant { + pub const VARIANTS: [TypeVariant; 6] = [ TypeVariant::SError, +TypeVariant::Multi, +TypeVariant::UnionKey, +TypeVariant::MyUnion, +TypeVariant::IntUnion, +TypeVariant::IntUnion2, ]; + pub const VARIANTS_STR: [&'static str; 6] = [ "SError", +"Multi", +"UnionKey", +"MyUnion", +"IntUnion", +"IntUnion2", ]; + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::SError => "SError", +Self::Multi => "Multi", +Self::UnionKey => "UnionKey", +Self::MyUnion => "MyUnion", +Self::IntUnion => "IntUnion", +Self::IntUnion2 => "IntUnion2", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 6] { + Self::VARIANTS + } + } + + impl Name for TypeVariant { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for TypeVariant { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl core::str::FromStr for TypeVariant { + type Err = Error; + #[allow(clippy::too_many_lines)] + fn from_str(s: &str) -> Result { + match s { + "SError" => Ok(Self::SError), +"Multi" => Ok(Self::Multi), +"UnionKey" => Ok(Self::UnionKey), +"MyUnion" => Ok(Self::MyUnion), +"IntUnion" => Ok(Self::IntUnion), +"IntUnion2" => Ok(Self::IntUnion2), + _ => Err(Error::Invalid), + } + } + } + + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr( + all(feature = "serde", feature = "alloc"), + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case"), + serde(untagged), + )] + pub enum Type { + SError(Box), +Multi(Box), +UnionKey(Box), +MyUnion(Box), +IntUnion(Box), +IntUnion2(Box), + } + + impl Type { + pub const VARIANTS: [TypeVariant; 6] = [ TypeVariant::SError, +TypeVariant::Multi, +TypeVariant::UnionKey, +TypeVariant::MyUnion, +TypeVariant::IntUnion, +TypeVariant::IntUnion2, ]; + pub const VARIANTS_STR: [&'static str; 6] = [ "SError", +"Multi", +"UnionKey", +"MyUnion", +"IntUnion", +"IntUnion2", ]; + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr(v: TypeVariant, r: &mut Limited) -> Result { + match v { + TypeVariant::SError => r.with_limited_depth(|r| Ok(Self::SError(Box::new(SError::read_xdr(r)?)))), +TypeVariant::Multi => r.with_limited_depth(|r| Ok(Self::Multi(Box::new(Multi::read_xdr(r)?)))), +TypeVariant::UnionKey => r.with_limited_depth(|r| Ok(Self::UnionKey(Box::new(UnionKey::read_xdr(r)?)))), +TypeVariant::MyUnion => r.with_limited_depth(|r| Ok(Self::MyUnion(Box::new(MyUnion::read_xdr(r)?)))), +TypeVariant::IntUnion => r.with_limited_depth(|r| Ok(Self::IntUnion(Box::new(IntUnion::read_xdr(r)?)))), +TypeVariant::IntUnion2 => r.with_limited_depth(|r| Ok(Self::IntUnion2(Box::new(IntUnion2::read_xdr(r)?)))), + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + pub fn read_xdr_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let s = Self::read_xdr(v, r)?; + // Check that any further reads, such as this read of one byte, read no + // data, indicating EOF. If a byte is read the data is invalid. + if r.read(&mut [0u8; 1])? == 0 { + Ok(s) + } else { + Err(Error::Invalid) + } + } + + #[cfg(feature = "base64")] + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut Limited) -> Result { + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.limits.clone()); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::SError(Box::new(t))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Multi(Box::new(t))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + match v { + TypeVariant::SError => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::SError(Box::new(t.0))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::Multi(Box::new(t.0))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion(Box::new(t.0))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion2(Box::new(t.0))))), + } + } + + #[cfg(feature = "base64")] + #[allow(clippy::too_many_lines)] + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut Limited) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + match v { + TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::SError(Box::new(t))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::Multi(Box::new(t))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(dec, r.limits.clone()).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), + } + } + + #[cfg(feature = "std")] + pub fn from_xdr>(v: TypeVariant, bytes: B, limits: Limits) -> Result { + let mut cursor = Limited::new(Cursor::new(bytes.as_ref()), limits); + let t = Self::read_xdr_to_end(v, &mut cursor)?; + Ok(t) + } + + #[cfg(feature = "base64")] + pub fn from_xdr_base64(v: TypeVariant, b64: impl AsRef<[u8]>, limits: Limits) -> Result { + let mut b64_reader = Cursor::new(b64); + let mut dec = Limited::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), limits); + let t = Self::read_xdr_to_end(v, &mut dec)?; + Ok(t) + } + + #[cfg(all(feature = "std", feature = "serde_json"))] + #[allow(clippy::too_many_lines)] + pub fn read_json(v: TypeVariant, r: impl Read) -> Result { + match v { + TypeVariant::SError => Ok(Self::SError(Box::new(serde_json::from_reader(r)?))), +TypeVariant::Multi => Ok(Self::Multi(Box::new(serde_json::from_reader(r)?))), +TypeVariant::UnionKey => Ok(Self::UnionKey(Box::new(serde_json::from_reader(r)?))), +TypeVariant::MyUnion => Ok(Self::MyUnion(Box::new(serde_json::from_reader(r)?))), +TypeVariant::IntUnion => Ok(Self::IntUnion(Box::new(serde_json::from_reader(r)?))), +TypeVariant::IntUnion2 => Ok(Self::IntUnion2(Box::new(serde_json::from_reader(r)?))), + } + } + + #[cfg(feature = "alloc")] + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> &dyn core::any::Any { + #[allow(clippy::match_same_arms)] + match self { + Self::SError(ref v) => v.as_ref(), +Self::Multi(ref v) => v.as_ref(), +Self::UnionKey(ref v) => v.as_ref(), +Self::MyUnion(ref v) => v.as_ref(), +Self::IntUnion(ref v) => v.as_ref(), +Self::IntUnion2(ref v) => v.as_ref(), + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn name(&self) -> &'static str { + match self { + Self::SError(_) => "SError", +Self::Multi(_) => "Multi", +Self::UnionKey(_) => "UnionKey", +Self::MyUnion(_) => "MyUnion", +Self::IntUnion(_) => "IntUnion", +Self::IntUnion2(_) => "IntUnion2", + } + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variants() -> [TypeVariant; 6] { + Self::VARIANTS + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub const fn variant(&self) -> TypeVariant { + match self { + Self::SError(_) => TypeVariant::SError, +Self::Multi(_) => TypeVariant::Multi, +Self::UnionKey(_) => TypeVariant::UnionKey, +Self::MyUnion(_) => TypeVariant::MyUnion, +Self::IntUnion(_) => TypeVariant::IntUnion, +Self::IntUnion2(_) => TypeVariant::IntUnion2, + } + } + } + + impl Name for Type { + #[must_use] + fn name(&self) -> &'static str { + Self::name(self) + } + } + + impl Variants for Type { + fn variants() -> slice::Iter<'static, TypeVariant> { + Self::VARIANTS.iter() + } + } + + impl WriteXdr for Type { + #[cfg(feature = "std")] + #[allow(clippy::too_many_lines)] + fn write_xdr(&self, w: &mut Limited) -> Result<()> { + match self { + Self::SError(v) => v.write_xdr(w), +Self::Multi(v) => v.write_xdr(w), +Self::UnionKey(v) => v.write_xdr(w), +Self::MyUnion(v) => v.write_xdr(w), +Self::IntUnion(v) => v.write_xdr(w), +Self::IntUnion2(v) => v.write_xdr(w), + } + } + }