@@ -22,16 +22,21 @@ import com.splendo.kaluga.base.bytes.ByteOrder
2222import com.splendo.kaluga.base.bytes.buildByteArray
2323import com.splendo.kaluga.base.bytes.toByteArray
2424import kotlinx.serialization.SerializationException
25+ import kotlin.math.ceil
2526
2627/* *
2728 * Builder for creating a [ByteArray] from a [BluetoothBinaryDescriptor]
2829 */
2930internal interface BinaryBuilder {
31+
32+ val expectedSize: Int
33+
3034 fun addFlag (index : Int , value : Boolean )
31- fun addAction (action : ByteArrayBuilder .() -> Unit )
35+ fun addBit (value : Boolean )
36+ fun addAction (expectedSize : Int , action : ByteArrayBuilder .() -> Unit )
3237 fun makeUnconstrained ()
3338
34- fun build (): ByteArray
39+ fun ByteArrayBuilder. build ()
3540}
3641
3742/* *
@@ -46,17 +51,44 @@ class DataAfterUnconstrainedData(override val message: String?) : SerializationE
4651internal abstract class StructureBinaryBuilder (val binaryDescriptor : BluetoothBinaryDescriptor , flagBitsSize : Int , private val onUnconstrained : () -> Unit ) : BinaryBuilder {
4752
4853 val flagBits: MutableList <Boolean > = MutableList (flagBitsSize.coerceAtLeast(0 )) { false }
54+
55+ override val expectedSize: Int
56+ get() = totalBodySize +
57+ (binaryDescriptor.structureSettings.checksumAlgorithm?.byteWidth ? : 0 ) +
58+ (binaryDescriptor.structureSettings.prefix?.array?.size ? : 0 ) +
59+ (binaryDescriptor.structureSettings.postfix?.array?.size ? : 0 )
60+ private var currentBits: Int = flagBitsSize % Byte .SIZE_BITS
61+ private var expectedBodySize: Int = flagBitsSize / Byte .SIZE_BITS
62+
63+ private val totalBodySize: Int get() = expectedBodySize +
64+ if (currentBits > 0 ) 1 else 0
4965 private val actions = mutableListOf<ByteArrayBuilder .() - > Unit > ()
5066 private var isOfUnconstrainedSize: Boolean = false
5167
5268 override fun addFlag (index : Int , value : Boolean ) {
5369 flagBits[index] = value
5470 }
5571
56- override fun addAction (action : ByteArrayBuilder .() -> Unit ) {
72+ override fun addBit (value : Boolean ) {
73+ currentBits++
74+ if (currentBits == Byte .SIZE_BITS ) {
75+ expectedBodySize++
76+ currentBits = 0
77+ }
78+ actions + = {
79+ add(value)
80+ }
81+ }
82+
83+ override fun addAction (expectedSize : Int , action : ByteArrayBuilder .() -> Unit ) {
5784 if (isOfUnconstrainedSize) {
5885 throw DataAfterUnconstrainedData (" Attempted to add data after data of an unconstrained size" )
5986 }
87+ if (currentBits > 0 ) {
88+ expectedBodySize++
89+ currentBits = 0
90+ }
91+ expectedBodySize + = expectedSize
6092 actions + = action
6193 }
6294
@@ -65,24 +97,18 @@ internal abstract class StructureBinaryBuilder(val binaryDescriptor: BluetoothBi
6597 isOfUnconstrainedSize = true
6698 }
6799
68- override fun build (): ByteArray {
69- // The body is the flag bits + remaining body. This is also the part used for checksum verification
70- val body = buildByteArray(binaryDescriptor.byteOrder, binaryDescriptor.expectedSize) {
71- flagBits.forEach {
72- add(it)
73- }
74- actions.forEach { apply (it) }
75- }
76- val crc = binaryDescriptor.structureSettings.checksumAlgorithm
77- val additionalBytes = listOfNotNull(binaryDescriptor.structureSettings.prefix, binaryDescriptor.structureSettings.postfix).sumOf { it.array.size }
78-
79- // Full data consists of prefix + body + checksum + postfix
80- return buildByteArray(binaryDescriptor.byteOrder, binaryDescriptor.expectedSize + (crc?.byteWidth ? : 0 ) + additionalBytes) {
100+ override fun ByteArrayBuilder.build () {
101+ if (byteOrder == binaryDescriptor.byteOrder) {
81102 binaryDescriptor.structureSettings.prefix?.let {
82103 add(it.array)
83104 }
84- add(body)
85- crc?.let {
105+ val crc = binaryDescriptor.structureSettings.checksumAlgorithm
106+ if (crc != null ) {
107+ // The body is the flag bits + remaining body. This is also the part used for checksum verification
108+ val body = buildByteArray(binaryDescriptor.byteOrder, totalBodySize) {
109+ buildBody()
110+ }
111+ add(body)
86112 // Calculate checksum if necessary
87113 val crcBytes = crc.compute(body).toByteArray(ByteOrder .LEAST_SIGNIFICANT_FIRST )
88114 for (i in 0 .. < crc.byteWidth) {
@@ -91,10 +117,31 @@ internal abstract class StructureBinaryBuilder(val binaryDescriptor: BluetoothBi
91117 ByteOrder .LEAST_SIGNIFICANT_FIRST -> add(crcBytes[i])
92118 }
93119 }
120+ } else {
121+ buildBody()
94122 }
123+
95124 binaryDescriptor.structureSettings.postfix?.let {
96125 add(it.array)
97126 }
127+ } else {
128+ add(
129+ buildByteArray(binaryDescriptor.byteOrder, expectedSize) {
130+ build()
131+ },
132+ )
133+ }
134+ }
135+
136+ private fun ByteArrayBuilder.buildBody () {
137+ flagBits.forEach {
138+ add(it)
139+ }
140+ actions.forEach { apply (it) }
141+ if (currentBits > 0 ) {
142+ repeat(Byte .SIZE_BITS - currentBits) {
143+ add(false )
144+ }
98145 }
99146 }
100147}
@@ -135,22 +182,24 @@ class UnexpectedNullTermination(override val message: String) : SerializationExc
135182/* *
136183 * A [BinaryBuilder] to build data for a collection (List/Map) structure
137184 */
138- internal abstract class CollectionBinaryBuilder (
139- private val byteOrder : ByteOrder ,
140- private val classBuilders : List <ItemBinaryBuilder >,
141- private val expectedSize : Int ,
142- private val isNullTerminated : Boolean ,
143- ) : BinaryBuilder {
185+ internal abstract class CollectionBinaryBuilder (private val byteOrder : ByteOrder , private val classBuilders : List <ItemBinaryBuilder >, private val isNullTerminated : Boolean ) :
186+ BinaryBuilder {
144187 private var currentIndex = 0
145188 val currentClassBuilder: ItemBinaryBuilder get() = classBuilders[currentIndex]
146189
190+ override val expectedSize: Int get() = classBuilders.sumOf { it.expectedSize }
191+
147192 fun setIndex (index : Int ): BluetoothBinaryDescriptor {
148193 currentIndex = index
149194 return currentClassBuilder.binaryDescriptor
150195 }
151196
152- override fun addAction (action : ByteArrayBuilder .() -> Unit ) {
153- currentClassBuilder.addAction(action)
197+ override fun addBit (value : Boolean ) {
198+ currentClassBuilder.addBit(value)
199+ }
200+
201+ override fun addAction (expectedSize : Int , action : ByteArrayBuilder .() -> Unit ) {
202+ currentClassBuilder.addAction(expectedSize, action)
154203 }
155204
156205 override fun addFlag (index : Int , value : Boolean ) {
@@ -161,19 +210,33 @@ internal abstract class CollectionBinaryBuilder(
161210 currentClassBuilder.makeUnconstrained()
162211 }
163212
164- override fun build (): ByteArray = if (classBuilders.isEmpty()) {
165- byteArrayOf()
166- } else {
167- buildByteArray(byteOrder, expectedSize) {
213+ override fun ByteArrayBuilder.build () {
214+ if (byteOrder == this @CollectionBinaryBuilder.byteOrder) {
168215 classBuilders.forEachIndexed { index, classBuilder ->
169- val value = classBuilder.build()
170-
171- // Ensure no unexpected null termination occurs
172- if (isNullTerminated && classBuilder.binaryDescriptor.fieldIndex == 0 && classBuilder.checkIfStartsWithNull(value, byteOrder)) {
173- throw UnexpectedNullTermination (" The element at $index starts with Null Byte in a Null Terminated List" )
216+ if (isNullTerminated && classBuilder.binaryDescriptor.fieldIndex == 0 ) {
217+ val value = buildByteArray(expectedSize = classBuilder.expectedSize) {
218+ with (classBuilder) {
219+ build()
220+ }
221+ }
222+ if (classBuilder.checkIfStartsWithNull(value, byteOrder)) {
223+ throw UnexpectedNullTermination (" The element at $index starts with Null Byte in a Null Terminated List" )
224+ }
225+ add(value)
226+ } else {
227+ with (classBuilder) {
228+ build()
229+ }
174230 }
175- add(value)
176231 }
232+ } else {
233+ add(
234+ with (this @CollectionBinaryBuilder) {
235+ buildByteArray(byteOrder, expectedSize) {
236+ build()
237+ }
238+ },
239+ )
177240 }
178241 }
179242}
@@ -187,7 +250,6 @@ internal class ListBinaryBuilder(binaryDescriptor: BluetoothBinaryDescriptor, si
187250 MutableList (size) {
188251 ItemBinaryBuilder (binaryDescriptor.children.first(), onUnconstrained)
189252 },
190- binaryDescriptor.children.first().expectedSize * size,
191253 isNullTerminated,
192254 )
193255
@@ -201,6 +263,5 @@ internal class MapBinaryBuilder(binaryDescriptor: BluetoothBinaryDescriptor, siz
201263 val index = it % 2
202264 ItemBinaryBuilder (binaryDescriptor.children[index], onUnconstrained)
203265 },
204- (binaryDescriptor.children[0 ].expectedSize + binaryDescriptor.children[1 ].expectedSize) * size,
205266 isNullTerminated,
206267 )
0 commit comments