Skip to content

Commit 65fbaad

Browse files
committed
tagged types
Signed-off-by: Dmitriy Khaustov aka xDimon <[email protected]>
1 parent 9253552 commit 65fbaad

File tree

5 files changed

+194
-5
lines changed

5 files changed

+194
-5
lines changed

include/scale/detail/collections.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <scale/detail/collections_type_traits.hpp>
1919
#include <scale/detail/decomposable_type_traits.hpp>
20+
#include <scale/detail/tagged.hpp>
2021

2122
namespace scale {
2223

include/scale/detail/tagged.hpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright Quadrivium LLC
3+
* All Rights Reserved
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @brief Provides encoding and decoding utilities for tagged types in SCALE
9+
* serialization.
10+
*
11+
* This file defines functions for encoding and decoding tagged types,
12+
* ensuring proper serialization and deserialization while preserving type
13+
* information.
14+
*/
15+
16+
#pragma once
17+
18+
#include <qtils/tagged.hpp>
19+
20+
#include <scale/detail/compact_integer.hpp>
21+
#include <scale/types.hpp>
22+
23+
namespace scale {
24+
25+
namespace detail::tagged {
26+
/// @brief Concept that ensures a type is not tagged.
27+
template <typename T>
28+
concept NoTagged = not qtils::is_tagged_v<T>;
29+
} // namespace detail::tagged
30+
31+
using detail::tagged::NoTagged;
32+
33+
/**
34+
* @brief Encodes a tagged type using SCALE encoding.
35+
*
36+
* @tparam T The tagged type to encode.
37+
* @param tagged The value to encode.
38+
* @param encoder The encoder instance to write to.
39+
*/
40+
template <typename T>
41+
requires qtils::is_tagged_v<T>
42+
and (not detail::compact_integer::CompactInteger<T>)
43+
void encode(const T &tagged, ScaleEncoder auto &encoder) {
44+
return encode(untagged(tagged), encoder);
45+
}
46+
47+
/**
48+
* @brief Decodes a tagged type using SCALE decoding.
49+
*
50+
* @tparam T The tagged type to decode.
51+
* @param tagged The value to decode into.
52+
* @param decoder The decoder instance to read from.
53+
*/
54+
template <typename T>
55+
requires qtils::is_tagged_v<T>
56+
and (not detail::compact_integer::CompactInteger<T>)
57+
void decode(T &tagged, ScaleDecoder auto &decoder) {
58+
return decode(untagged(tagged), decoder);
59+
}
60+
61+
} // namespace scale

include/scale/scale.hpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
/**
8+
* @brief Combines all headers to use existing or build custom SCALE
9+
* serialization and deserialization mechanisms
10+
*/
11+
712
#pragma once
813

914
#include <qtils/outcome.hpp>
@@ -23,13 +28,31 @@
2328
#include <scale/detail/optional.hpp>
2429
#include <scale/detail/smart_pointers.hpp>
2530
#include <scale/detail/variant.hpp>
31+
#include <scale/detail/tagged.hpp>
32+
33+
/**
34+
* @brief Provides implementations for SCALE encoding and decoding.
35+
*
36+
* This file defines the memory-based SCALE serialization and deserialization
37+
* mechanisms using the Outcome result type to handle errors gracefully.
38+
*/
2639

2740
namespace scale::impl {
2841

29-
// Well done implementation using outcome::result and data in memory
42+
/**
43+
* @namespace memory
44+
* @brief Memory-based implementation of SCALE encoding and decoding.
45+
*/
3046
namespace memory {
3147
using Encoder = Encoder<backend::ToBytes>;
3248

49+
/**
50+
* @brief Encodes a value using SCALE encoding.
51+
*
52+
* @tparam T The type of the value to encode.
53+
* @param value The value to encode.
54+
* @return A result containing the encoded byte vector or an error.
55+
*/
3356
template <typename T>
3457
outcome::result<std::vector<uint8_t>> encode(T &&value) {
3558
Encoder encoder;
@@ -43,6 +66,13 @@ namespace scale::impl {
4366

4467
using Decoder = Decoder<backend::FromBytes>;
4568

69+
/**
70+
* @brief Decodes a value using SCALE decoding.
71+
*
72+
* @tparam T The type of the value to decode.
73+
* @param bytes The byte span containing encoded data.
74+
* @return A result containing the decoded value or an error.
75+
*/
4676
template <typename T>
4777
outcome::result<T> decode(ConstSpanOfBytes bytes) {
4878
Decoder decoder{bytes};

include/scale/types.hpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@
2525

2626
namespace scale {
2727

28-
/// @brief Concept that ensures a type is not tagged.
29-
template <typename T>
30-
concept NoTagged = not qtils::is_tagged_v<T>;
31-
3228
// Encoding components
3329

3430
/**

test/scale_tagged_test.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
2+
/**
3+
* Copyright Quadrivium LLC
4+
* All Rights Reserved
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
/**
9+
* @brief Unit tests for encoding and decoding tagged types using SCALE.
10+
*
11+
* This file contains tests for encoding and decoding tagged types,
12+
* ensuring that serialization and deserialization work as expected.
13+
*/
14+
15+
#include <gtest/gtest.h>
16+
17+
#include <qtils/test/outcome.hpp>
18+
#include <qtils/tagged.hpp>
19+
20+
#include <scale/scale.hpp>
21+
22+
using scale::ByteArray;
23+
using scale::impl::memory::decode;
24+
using scale::impl::memory::encode;
25+
26+
using TaggedString = qtils::Tagged<std::string, class String>;
27+
using TaggedInteger = qtils::Tagged<uint32_t, class Integer>;
28+
29+
/**
30+
* @brief Tests encoding of a tagged string.
31+
*
32+
* @given A string and its tagged equivalent.
33+
* @when Encoding is applied.
34+
* @then The serialized value of the tagged string matches the original string.
35+
*/
36+
TEST(Tagged, StringEncode) {
37+
std::string string = "hello world";
38+
TaggedString tagged = "hello world";
39+
40+
ASSERT_OUTCOME_SUCCESS(encoded_original, encode(string));
41+
ASSERT_OUTCOME_SUCCESS(encoded_tagged, encode(tagged));
42+
43+
ASSERT_EQ(encoded_tagged, encoded_original);
44+
}
45+
46+
/**
47+
* @brief Tests decoding of a tagged string.
48+
*
49+
* @given A byte sequence containing an encoded string.
50+
* @when Decoding is applied.
51+
* @then The decoded value matches the original string.
52+
*/
53+
TEST(Tagged, StringDecode) {
54+
std::string original = "hello world";
55+
ASSERT_OUTCOME_SUCCESS(encoded, encode(original));
56+
57+
ASSERT_OUTCOME_SUCCESS(decoded, decode<TaggedString>(encoded));
58+
59+
ASSERT_EQ(untagged(decoded), original);
60+
}
61+
62+
/**
63+
* @brief Tests encoding and decoding of a tagged integer.
64+
*
65+
* @given A tagged integer.
66+
* @when The integer is encoded and then decoded.
67+
* @then The decoded value matches the original integer.
68+
*/
69+
TEST(Tagged, StringEncodeAndDecode) {
70+
TaggedString original = "hello world";
71+
72+
ASSERT_OUTCOME_SUCCESS(encoded, encode(original));
73+
ASSERT_OUTCOME_SUCCESS(decoded, decode<TaggedString>(encoded));
74+
ASSERT_EQ(decoded, original);
75+
}
76+
77+
TEST(Tagged, IntegerEncode) {
78+
uint32_t integer = 123456789;
79+
TaggedInteger tagged(123456789);
80+
81+
ASSERT_OUTCOME_SUCCESS(encoded_original, encode(integer));
82+
ASSERT_OUTCOME_SUCCESS(encoded_tagged, encode(tagged));
83+
84+
ASSERT_EQ(encoded_tagged, encoded_original);
85+
}
86+
87+
/**
88+
* @brief Tests decoding of a tagged integer.
89+
*
90+
* @given A byte sequence containing an encoded integer.
91+
* @when Decoding is applied.
92+
* @then The decoded value matches the original integer.
93+
*/
94+
TEST(Tagged, IntegerDecode) {
95+
uint32_t original = 123456789;
96+
ASSERT_OUTCOME_SUCCESS(encoded, encode(original));
97+
98+
ASSERT_OUTCOME_SUCCESS(decoded, decode<TaggedInteger>(encoded));
99+
100+
ASSERT_EQ(untagged(decoded), original);
101+
}

0 commit comments

Comments
 (0)