5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
+ // Result<..., ()> is used. But we don't have more error info. https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err.
9
+ // We may want to change () to something like godot::meta::IoError, or a domain-specific one, in the future.
10
+ #![ allow( clippy:: result_unit_err) ]
11
+
8
12
use godot_ffi as sys;
9
13
10
14
use crate :: builtin:: * ;
@@ -835,119 +839,184 @@ impl<'r> PackedTraits for meta::CowArg<'r, GString> {
835
839
836
840
macro_rules! declare_encode_decode {
837
841
// $Via could be inferred, but ensures we have the correct type expectations.
838
- ( $Ty: ty, $encode_fn: ident, $decode_fn: ident, $Via: ty) => {
839
- #[ doc = concat!( "Encodes a value of type `" , stringify!( $Ty) , "` at position `byte_offset`." ) ]
842
+ ( $Ty: ty, $bytes: literal, $encode_fn: ident, $decode_fn: ident, $Via: ty) => {
843
+ #[ doc = concat!( "Encodes `" , stringify!( $Ty) , "` as " , stringify!( $bytes) , " byte(s) at position `byte_offset`." ) ]
844
+ ///
845
+ /// Returns `Err` if there is not enough space left to write the value, and does nothing in that case.
840
846
///
841
- /// Godot will print an error on encode failure (not enough space left). If you want to detect this programmatically,
842
- /// you need to check the offset manually -- or write directly to a Rust data structure, such as `Vec<u8>`.
843
- pub fn $encode_fn( & self , byte_offset: usize , value: $Ty) {
844
- // We trust Godot that indeed only fitting values are returned.
847
+ /// **Note:** byte order and encoding pattern is an implementation detail. For portable byte representation and faster encoding, use
848
+ /// [`as_mut_slice()`][Self::as_mut_slice] and the various Rust standard APIs such as
849
+ #[ doc = concat!( "[`" , stringify!( $Ty) , "::to_be_bytes()`]." ) ]
850
+ pub fn $encode_fn( & mut self , byte_offset: usize , value: $Ty) -> Result <( ) , ( ) > {
851
+ // sys::static_assert!(std::mem::size_of::<$Ty>() == $bytes); -- used for testing, can't keep enabled due to half-floats.
852
+
853
+ if byte_offset + $bytes > self . len( ) {
854
+ return Err ( ( ) ) ;
855
+ }
856
+
845
857
self . as_inner( )
846
858
. $encode_fn( byte_offset as i64 , value as $Via) ;
859
+ Ok ( ( ) )
847
860
}
848
861
849
- #[ doc = concat!( "Decodes a value of type `" , stringify!( $Ty) , "` at position `byte_offset`." ) ]
862
+ #[ doc = concat!( "Decodes `" , stringify!( $Ty) , "` from " , stringify!( $bytes) , " byte(s) at position `byte_offset`." ) ]
863
+ ///
864
+ /// Returns `Err` if there is not enough space left to read the value. In case Godot has other error conditions for decoding, it may
865
+ /// return zero and print an error.
850
866
///
851
- /// Godot will print an error on decode failure (not enough space left), and return `0`. If you want to detect this programmatically,
852
- /// you need to check the offset manually -- or convert the packed array to a Rust data structure, such as `Vec<u8>`.
853
- pub fn $decode_fn( & self , byte_offset: usize ) -> $Ty {
854
- // We trust Godot that indeed only fitting values are returned.
867
+ /// **Note:** byte order and encoding pattern is an implementation detail. For portable byte representation and faster decoding, use
868
+ /// [`as_slice()`][Self::as_slice] and the various Rust standard APIs such as
869
+ #[ doc = concat!( "[`" , stringify!( $Ty) , "::from_be_bytes()`]." ) ]
870
+ pub fn $decode_fn( & self , byte_offset: usize ) -> Result <$Ty, ( ) > {
871
+ if byte_offset + $bytes > self . len( ) {
872
+ return Err ( ( ) ) ;
873
+ }
874
+
855
875
let decoded: $Via = self . as_inner( ) . $decode_fn( byte_offset as i64 ) ;
856
- decoded as $Ty
876
+ Ok ( decoded as $Ty)
857
877
}
858
878
} ;
859
879
}
860
880
861
881
impl PackedByteArray {
862
- declare_encode_decode ! ( u8 , encode_u8, decode_u8, i64 ) ;
863
- declare_encode_decode ! ( i8 , encode_s8, decode_s8, i64 ) ;
864
- declare_encode_decode ! ( u16 , encode_u16, decode_u16, i64 ) ;
865
- declare_encode_decode ! ( i16 , encode_s16, decode_s16, i64 ) ;
866
- declare_encode_decode ! ( u32 , encode_u32, decode_u32, i64 ) ;
867
- declare_encode_decode ! ( i32 , encode_s32, decode_s32, i64 ) ;
868
- declare_encode_decode ! ( u64 , encode_u64, decode_u64, i64 ) ;
869
- declare_encode_decode ! ( i64 , encode_s64, decode_s64, i64 ) ;
870
- declare_encode_decode ! ( f32 , encode_half, decode_half, f64 ) ;
871
- declare_encode_decode ! ( f32 , encode_float, decode_float, f64 ) ;
872
- declare_encode_decode ! ( f64 , encode_double, decode_double, f64 ) ;
873
-
874
- /// Encodes a `Variant` as bytes. Returns number of bytes written.
882
+ declare_encode_decode ! ( u8 , 1 , encode_u8, decode_u8, i64 ) ;
883
+ declare_encode_decode ! ( i8 , 1 , encode_s8, decode_s8, i64 ) ;
884
+ declare_encode_decode ! ( u16 , 2 , encode_u16, decode_u16, i64 ) ;
885
+ declare_encode_decode ! ( i16 , 2 , encode_s16, decode_s16, i64 ) ;
886
+ declare_encode_decode ! ( u32 , 4 , encode_u32, decode_u32, i64 ) ;
887
+ declare_encode_decode ! ( i32 , 4 , encode_s32, decode_s32, i64 ) ;
888
+ declare_encode_decode ! ( u64 , 8 , encode_u64, decode_u64, i64 ) ;
889
+ declare_encode_decode ! ( i64 , 8 , encode_s64, decode_s64, i64 ) ;
890
+ declare_encode_decode ! ( f32 , 2 , encode_half, decode_half, f64 ) ;
891
+ declare_encode_decode ! ( f32 , 4 , encode_float, decode_float, f64 ) ;
892
+ declare_encode_decode ! ( f64 , 8 , encode_double, decode_double, f64 ) ;
893
+
894
+ /// Encodes a `Variant` as bytes. Returns number of bytes written, or `Err` on encoding failure .
875
895
///
876
896
/// Sufficient space must be allocated, depending on the encoded variant's size. If `allow_objects` is false, [`VariantType::OBJECT`] values
877
897
/// are not permitted and will instead be serialized as ID-only. You should set `allow_objects` to false by default.
878
898
pub fn encode_var (
879
- & self ,
899
+ & mut self ,
880
900
byte_offset : usize ,
881
901
value : impl AsArg < Variant > ,
882
902
allow_objects : bool ,
883
- ) -> usize {
903
+ ) -> Result < usize , ( ) > {
884
904
meta:: arg_into_ref!( value) ;
885
905
886
906
let bytes_written: i64 =
887
907
self . as_inner ( )
888
908
. encode_var ( byte_offset as i64 , value, allow_objects) ;
889
- bytes_written as usize
890
- }
891
909
892
- /// Returns `true` if a valid `Variant` value can be decoded at `byte_offset`.
893
- ///
894
- /// Returns `false` otherwise, or when the value is of type [`VariantType::OBJECT`] and `allow_objects` is `false`.
895
- ///
896
- /// # Security
897
- /// You should set `allow_objects` to `false` unless you have a good reason not to. Decoding objects (e.g. coming from remote sources)
898
- /// can cause arbitrary code execution.
899
- pub fn has_encoded_var ( & self , byte_offset : usize , allow_objects : bool ) -> bool {
900
- self . as_inner ( )
901
- . has_encoded_var ( byte_offset as i64 , allow_objects)
910
+ if bytes_written == -1 {
911
+ Err ( ( ) )
912
+ } else {
913
+ Ok ( bytes_written as usize )
914
+ }
902
915
}
903
916
904
- /// Decodes a `Variant` from bytes and returns it.
917
+ /// Decodes a `Variant` from bytes and returns it, alongside the number of bytes read .
905
918
///
906
- /// Returns [`Variant::nil()`] if a valid variant can't be decoded, or the value is of type [`VariantType::OBJECT`] and `allow_objects`
907
- /// is `false` .
919
+ /// Returns `Err` on decoding error. If you store legit `NIL` variants inside the byte array, use
920
+ /// [`decode_var_allow_nil()`][Self::decode_var_allow_nil] instead .
908
921
///
909
- /// To know how many bytes the decoded variant took, you need to separately call [`decode_var_size()`][Self::decode_var_size].
922
+ /// # API design
923
+ /// Godot offers three separate methods `decode_var()`, `decode_var_size()` and `has_encoded_var()`. That comes with several problems:
924
+ /// - `has_encoded_var()` is practically useless, because it performs the full decoding work and then throws away the variant.
925
+ /// `decode_var()` can do all that and more.
926
+ /// - Both `has_encoded_var()` and `decode_var_size()` are unreliable. They don't tell whether an actual variant has been written at
927
+ /// the location. They interpret garbage as `Variant::nil()` and return `true` or `4`, respectively. This can very easily cause bugs
928
+ /// because surprisingly, some users may expect that `has_encoded_var()` returns _whether a variant has been encoded_.
929
+ /// - The underlying C++ implementation has all the necessary information (whether a variant is there, how big it is and its value) but the
930
+ /// GDExtension API returns only one info at a time, requiring re-decoding on each call.
931
+ ///
932
+ /// godot-rust mitigates this somewhat, with the following design:
933
+ /// - `decode_var()` treats all `NIL`s as errors. This is most often the desired behavior, and if not, `decode_var_allow_nil()` can be used.
934
+ /// It's also the only way to detect errors at all -- once you store legit `NIL` values, you can no longer differentiate them from garbage.
935
+ /// - `decode_var()` returns both the decoded variant and its size. This requires two decoding runs, but only if the variant is actually
936
+ /// valid. Again, in many cases, a user needs the size to know where follow-up data in the buffer starts.
937
+ /// - `decode_var_size()` and `has_encoded_var()` are not exposed.
910
938
///
911
939
/// # Security
912
940
/// You should set `allow_objects` to `false` unless you have a good reason not to. Decoding objects (e.g. coming from remote sources)
913
941
/// can cause arbitrary code execution.
914
- pub fn decode_var ( & self , byte_offset : usize , allow_objects : bool ) -> Variant {
915
- self . as_inner ( )
916
- . decode_var ( byte_offset as i64 , allow_objects)
942
+ #[ doc( alias = "has_encoded_var" , alias = "decode_var_size" ) ]
943
+ #[ inline]
944
+ pub fn decode_var (
945
+ & self ,
946
+ byte_offset : usize ,
947
+ allow_objects : bool ,
948
+ ) -> Result < ( Variant , usize ) , ( ) > {
949
+ let variant = self
950
+ . as_inner ( )
951
+ . decode_var ( byte_offset as i64 , allow_objects) ;
952
+
953
+ if variant. is_nil ( ) {
954
+ return Err ( ( ) ) ;
955
+ }
956
+
957
+ // It's unfortunate that this does another full decoding, but decode_var() is barely useful without also knowing the size, as it won't
958
+ // be possible to know where to start reading any follow-up data. Furthermore, decode_var_size() often returns true when there's in fact
959
+ // no variant written at that place, it just interprets "nil", treats it as valid, and happily returns 4 bytes.
960
+ //
961
+ // So we combine the two calls for the sake of convenience and to avoid accidental usage.
962
+ let size: i64 = self
963
+ . as_inner ( )
964
+ . decode_var_size ( byte_offset as i64 , allow_objects) ;
965
+ debug_assert_ne ! ( size, -1 ) ; // must not happen if we just decoded variant.
966
+
967
+ Ok ( ( variant, size as usize ) )
917
968
}
918
969
919
- /// Decodes a `Variant` from bytes and returns it.
970
+ /// Unreliable `Variant` decoding, allowing `NIL`.
971
+ ///
972
+ /// <div class="warning">
973
+ /// <p>This method is highly unreliable and will try to interpret anything into variants, even zeroed memory or random byte patterns.
974
+ /// Only use it if you need a 1:1 equivalent of Godot's <code>decode_var()</code> and <code>decode_var_size()</code> functions.</p>
920
975
///
921
- /// Returns [`Variant::nil()`] if a valid variant can't be decoded, or the value is of type [`VariantType::OBJECT`] and `allow_objects`
922
- /// is `false`.
976
+ /// <p>In the majority of cases, <a href="struct.PackedByteArray.html#method.decode_var" title="method godot::builtin::PackedByteArray::decode_var">
977
+ /// <code>decode_var()</code></a> is the better choice, as it’s much easier to use correctly. See also its section about the rationale
978
+ /// behind the current API design.</p>
979
+ /// </div>
923
980
///
924
- /// This method is designed to be called in combination with [`decode_var()`][Self::decode_var].
981
+ /// Returns a tuple of two elements:
982
+ /// 1. the decoded variant. This is [`Variant::nil()`] if a valid variant can't be decoded, or the value is of type [`VariantType::OBJECT`]
983
+ /// and `allow_objects` is `false`.
984
+ /// 2. The number of bytes the variant occupies. This is `0` if running out of space, but most other failures are not recognized.
925
985
///
926
986
/// # Security
927
987
/// You should set `allow_objects` to `false` unless you have a good reason not to. Decoding objects (e.g. coming from remote sources)
928
988
/// can cause arbitrary code execution.
929
- pub fn decode_var_size ( & self , byte_offset : usize , allow_objects : bool ) -> usize {
930
- let size: i64 = self
931
- . as_inner ( )
932
- . decode_var_size ( byte_offset as i64 , allow_objects) ;
989
+ #[ inline]
990
+ pub fn decode_var_allow_nil (
991
+ & self ,
992
+ byte_offset : usize ,
993
+ allow_objects : bool ,
994
+ ) -> ( Variant , usize ) {
995
+ let byte_offset = byte_offset as i64 ;
996
+
997
+ let variant = self . as_inner ( ) . decode_var ( byte_offset, allow_objects) ;
998
+ let decoded_size = self . as_inner ( ) . decode_var_size ( byte_offset, allow_objects) ;
999
+ let decoded_size = decoded_size. try_into ( ) . unwrap_or_else ( |_| {
1000
+ panic ! ( "unexpected value {decoded_size} returned from decode_var_size()" )
1001
+ } ) ;
933
1002
934
- size as usize
1003
+ ( variant , decoded_size )
935
1004
}
936
1005
937
1006
/// Returns a new `PackedByteArray`, with the data of this array compressed.
938
1007
///
939
- /// On failure, Godot prints an error and this method returns `None `. (Note that any empty results coming from Godot are mapped to `None `
1008
+ /// On failure, Godot prints an error and this method returns `Err `. (Note that any empty results coming from Godot are mapped to `Err `
940
1009
/// in Rust.)
941
- pub fn compress ( & self , compression_mode : CompressionMode ) -> Option < PackedByteArray > {
1010
+ pub fn compress ( & self , compression_mode : CompressionMode ) -> Result < PackedByteArray , ( ) > {
942
1011
let compressed: PackedByteArray = self . as_inner ( ) . compress ( compression_mode. ord ( ) as i64 ) ;
943
- populated_or_none ( compressed)
1012
+ populated_or_err ( compressed)
944
1013
}
945
1014
946
1015
/// Returns a new `PackedByteArray`, with the data of this array decompressed.
947
1016
///
948
1017
/// Set `buffer_size` to the size of the uncompressed data.
949
1018
///
950
- /// On failure, Godot prints an error and this method returns `None `. (Note that any empty results coming from Godot are mapped to `None `
1019
+ /// On failure, Godot prints an error and this method returns `Err `. (Note that any empty results coming from Godot are mapped to `Err `
951
1020
/// in Rust.)
952
1021
///
953
1022
/// **Note:** Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate
@@ -956,12 +1025,12 @@ impl PackedByteArray {
956
1025
& self ,
957
1026
buffer_size : usize ,
958
1027
compression_mode : CompressionMode ,
959
- ) -> Option < PackedByteArray > {
1028
+ ) -> Result < PackedByteArray , ( ) > {
960
1029
let decompressed: PackedByteArray = self
961
1030
. as_inner ( )
962
1031
. decompress ( buffer_size as i64 , compression_mode. ord ( ) as i64 ) ;
963
1032
964
- populated_or_none ( decompressed)
1033
+ populated_or_err ( decompressed)
965
1034
}
966
1035
967
1036
/// Returns a new `PackedByteArray`, with the data of this array decompressed, and without fixed decompression buffer.
@@ -976,26 +1045,29 @@ impl PackedByteArray {
976
1045
/// `max_output_size`. Passing `None` will allow for unbounded output. If any positive value is passed, and the decompression exceeds that
977
1046
/// amount in bytes, then an error will be returned.
978
1047
///
1048
+ /// On failure, Godot prints an error and this method returns `Err`. (Note that any empty results coming from Godot are mapped to `Err`
1049
+ /// in Rust.)
1050
+ ///
979
1051
/// **Note:** Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate
980
1052
/// compression mode lacks a checksum or header.
981
1053
pub fn decompress_dynamic (
982
1054
& self ,
983
1055
max_output_size : Option < usize > ,
984
1056
compression_mode : CompressionMode ,
985
- ) -> Option < PackedByteArray > {
1057
+ ) -> Result < PackedByteArray , ( ) > {
986
1058
let max_output_size = max_output_size. map ( |i| i as i64 ) . unwrap_or ( -1 ) ;
987
1059
let decompressed: PackedByteArray = self
988
1060
. as_inner ( )
989
1061
. decompress_dynamic ( max_output_size, compression_mode. ord ( ) as i64 ) ;
990
1062
991
- populated_or_none ( decompressed)
1063
+ populated_or_err ( decompressed)
992
1064
}
993
1065
}
994
1066
995
- fn populated_or_none ( array : PackedByteArray ) -> Option < PackedByteArray > {
1067
+ fn populated_or_err ( array : PackedByteArray ) -> Result < PackedByteArray , ( ) > {
996
1068
if array. is_empty ( ) {
997
- None
1069
+ Err ( ( ) )
998
1070
} else {
999
- Some ( array)
1071
+ Ok ( array)
1000
1072
}
1001
1073
}
0 commit comments