Skip to content

Commit 1b2211c

Browse files
authored
Add helpers for finalizing Counter (#94)
1 parent fb5bc25 commit 1b2211c

File tree

4 files changed

+232
-2
lines changed

4 files changed

+232
-2
lines changed

library/core/api/core.api

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public final class org/kotlincrypto/core/Counter$Bit32 : org/kotlincrypto/core/C
2121
public fun <init> (III)V
2222
public synthetic fun copy ()Ljava/lang/Object;
2323
public fun copy ()Lorg/kotlincrypto/core/Counter$Bit32;
24+
public final fun final (I)Lorg/kotlincrypto/core/Counter$Bit32$Final;
2425
public final fun hi ()I
2526
public fun increment ()V
2627
public final fun lo ()I
@@ -30,6 +31,17 @@ public final class org/kotlincrypto/core/Counter$Bit32 : org/kotlincrypto/core/C
3031
public final class org/kotlincrypto/core/Counter$Bit32$Companion {
3132
}
3233

34+
public final class org/kotlincrypto/core/Counter$Bit32$Final {
35+
public final field hi I
36+
public final field lo I
37+
public final fun asBits ()Lorg/kotlincrypto/core/Counter$Bit32$Final;
38+
public final fun component1 ()I
39+
public final fun component2 ()I
40+
public fun equals (Ljava/lang/Object;)Z
41+
public fun hashCode ()I
42+
public fun toString ()Ljava/lang/String;
43+
}
44+
3345
public final class org/kotlincrypto/core/Counter$Bit64 : org/kotlincrypto/core/Counter {
3446
public static final field Companion Lorg/kotlincrypto/core/Counter$Bit64$Companion;
3547
public static final field MAX_INCREMENT J
@@ -38,6 +50,7 @@ public final class org/kotlincrypto/core/Counter$Bit64 : org/kotlincrypto/core/C
3850
public fun <init> (JJJ)V
3951
public synthetic fun copy ()Ljava/lang/Object;
4052
public fun copy ()Lorg/kotlincrypto/core/Counter$Bit64;
53+
public final fun final (I)Lorg/kotlincrypto/core/Counter$Bit64$Final;
4154
public final fun hi ()J
4255
public fun increment ()V
4356
public final fun lo ()J
@@ -47,6 +60,17 @@ public final class org/kotlincrypto/core/Counter$Bit64 : org/kotlincrypto/core/C
4760
public final class org/kotlincrypto/core/Counter$Bit64$Companion {
4861
}
4962

63+
public final class org/kotlincrypto/core/Counter$Bit64$Final {
64+
public final field hi J
65+
public final field lo J
66+
public final fun asBits ()Lorg/kotlincrypto/core/Counter$Bit64$Final;
67+
public final fun component1 ()J
68+
public final fun component2 ()J
69+
public fun equals (Ljava/lang/Object;)Z
70+
public fun hashCode ()I
71+
public fun toString ()Ljava/lang/String;
72+
}
73+
5074
public abstract interface annotation class org/kotlincrypto/core/ExperimentalKotlinCryptoApi : java/lang/annotation/Annotation {
5175
}
5276

library/core/api/core.klib.api

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,24 @@ sealed class org.kotlincrypto.core/Counter : org.kotlincrypto.core/Copyable<org.
5151
final fun <get-lo>(): kotlin/Int // org.kotlincrypto.core/Counter.Bit32.lo.<get-lo>|<get-lo>(){}[0]
5252

5353
final fun copy(): org.kotlincrypto.core/Counter.Bit32 // org.kotlincrypto.core/Counter.Bit32.copy|copy(){}[0]
54+
final fun final(kotlin/Int): org.kotlincrypto.core/Counter.Bit32.Final // org.kotlincrypto.core/Counter.Bit32.final|final(kotlin.Int){}[0]
5455
final fun increment() // org.kotlincrypto.core/Counter.Bit32.increment|increment(){}[0]
5556
final fun reset() // org.kotlincrypto.core/Counter.Bit32.reset|reset(){}[0]
5657

58+
final class Final { // org.kotlincrypto.core/Counter.Bit32.Final|null[0]
59+
final val hi // org.kotlincrypto.core/Counter.Bit32.Final.hi|{}hi[0]
60+
final fun <get-hi>(): kotlin/Int // org.kotlincrypto.core/Counter.Bit32.Final.hi.<get-hi>|<get-hi>(){}[0]
61+
final val lo // org.kotlincrypto.core/Counter.Bit32.Final.lo|{}lo[0]
62+
final fun <get-lo>(): kotlin/Int // org.kotlincrypto.core/Counter.Bit32.Final.lo.<get-lo>|<get-lo>(){}[0]
63+
64+
final fun asBits(): org.kotlincrypto.core/Counter.Bit32.Final // org.kotlincrypto.core/Counter.Bit32.Final.asBits|asBits(){}[0]
65+
final fun component1(): kotlin/Int // org.kotlincrypto.core/Counter.Bit32.Final.component1|component1(){}[0]
66+
final fun component2(): kotlin/Int // org.kotlincrypto.core/Counter.Bit32.Final.component2|component2(){}[0]
67+
final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core/Counter.Bit32.Final.equals|equals(kotlin.Any?){}[0]
68+
final fun hashCode(): kotlin/Int // org.kotlincrypto.core/Counter.Bit32.Final.hashCode|hashCode(){}[0]
69+
final fun toString(): kotlin/String // org.kotlincrypto.core/Counter.Bit32.Final.toString|toString(){}[0]
70+
}
71+
5772
final object Companion { // org.kotlincrypto.core/Counter.Bit32.Companion|null[0]
5873
final const val MAX_INCREMENT // org.kotlincrypto.core/Counter.Bit32.Companion.MAX_INCREMENT|{}MAX_INCREMENT[0]
5974
final fun <get-MAX_INCREMENT>(): kotlin/Int // org.kotlincrypto.core/Counter.Bit32.Companion.MAX_INCREMENT.<get-MAX_INCREMENT>|<get-MAX_INCREMENT>(){}[0]
@@ -73,9 +88,24 @@ sealed class org.kotlincrypto.core/Counter : org.kotlincrypto.core/Copyable<org.
7388
final fun <get-lo>(): kotlin/Long // org.kotlincrypto.core/Counter.Bit64.lo.<get-lo>|<get-lo>(){}[0]
7489

7590
final fun copy(): org.kotlincrypto.core/Counter.Bit64 // org.kotlincrypto.core/Counter.Bit64.copy|copy(){}[0]
91+
final fun final(kotlin/Int): org.kotlincrypto.core/Counter.Bit64.Final // org.kotlincrypto.core/Counter.Bit64.final|final(kotlin.Int){}[0]
7692
final fun increment() // org.kotlincrypto.core/Counter.Bit64.increment|increment(){}[0]
7793
final fun reset() // org.kotlincrypto.core/Counter.Bit64.reset|reset(){}[0]
7894

95+
final class Final { // org.kotlincrypto.core/Counter.Bit64.Final|null[0]
96+
final val hi // org.kotlincrypto.core/Counter.Bit64.Final.hi|{}hi[0]
97+
final fun <get-hi>(): kotlin/Long // org.kotlincrypto.core/Counter.Bit64.Final.hi.<get-hi>|<get-hi>(){}[0]
98+
final val lo // org.kotlincrypto.core/Counter.Bit64.Final.lo|{}lo[0]
99+
final fun <get-lo>(): kotlin/Long // org.kotlincrypto.core/Counter.Bit64.Final.lo.<get-lo>|<get-lo>(){}[0]
100+
101+
final fun asBits(): org.kotlincrypto.core/Counter.Bit64.Final // org.kotlincrypto.core/Counter.Bit64.Final.asBits|asBits(){}[0]
102+
final fun component1(): kotlin/Long // org.kotlincrypto.core/Counter.Bit64.Final.component1|component1(){}[0]
103+
final fun component2(): kotlin/Long // org.kotlincrypto.core/Counter.Bit64.Final.component2|component2(){}[0]
104+
final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core/Counter.Bit64.Final.equals|equals(kotlin.Any?){}[0]
105+
final fun hashCode(): kotlin/Int // org.kotlincrypto.core/Counter.Bit64.Final.hashCode|hashCode(){}[0]
106+
final fun toString(): kotlin/String // org.kotlincrypto.core/Counter.Bit64.Final.toString|toString(){}[0]
107+
}
108+
79109
final object Companion { // org.kotlincrypto.core/Counter.Bit64.Companion|null[0]
80110
final const val MAX_INCREMENT // org.kotlincrypto.core/Counter.Bit64.Companion.MAX_INCREMENT|{}MAX_INCREMENT[0]
81111
final fun <get-MAX_INCREMENT>(): kotlin/Long // org.kotlincrypto.core/Counter.Bit64.Companion.MAX_INCREMENT.<get-MAX_INCREMENT>|<get-MAX_INCREMENT>(){}[0]

library/core/src/commonMain/kotlin/org/kotlincrypto/core/Counter.kt

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public sealed class Counter private constructor(): Resettable, Copyable<Counter>
4242
/**
4343
* The maximum value for which [Bit32.incrementBy] can be set to.
4444
*
45-
* 1024^2 >> 1048576
45+
* 1024^2 = 1048576
4646
* */
4747
public const val MAX_INCREMENT: Int = 1048576 // Never decrease, only increase.
4848
}
@@ -104,11 +104,66 @@ public sealed class Counter private constructor(): Resettable, Copyable<Counter>
104104
if (lo == 0) hi++
105105
}
106106

107+
/**
108+
* Produces a final count, including any additional value needed to be
109+
* added (such as the size of buffered input).
110+
*
111+
* **NOTE:** [reset] is not called and the [Counter] is unaltered by
112+
* [additional] value.
113+
* */
114+
public fun final(additional: Int): Final {
115+
var lo = lo
116+
var hi = hi
117+
val lt0 = lo < 0
118+
lo += additional
119+
if (lt0 && lo >= 0) hi++
120+
return Final(lo = lo, hi = hi)
121+
}
122+
107123
public override fun reset() {
108124
lo = 0
109125
hi = 0
110126
}
111127

128+
/**
129+
* Holder of the count produced by [final]
130+
* */
131+
public class Final private constructor(
132+
@JvmField
133+
public val lo: Int,
134+
@JvmField
135+
public val hi: Int,
136+
private val isBits: Boolean,
137+
) {
138+
139+
internal constructor(lo: Int, hi: Int): this(lo, hi, isBits = false)
140+
141+
public operator fun component1(): Int = lo
142+
public operator fun component2(): Int = hi
143+
144+
/**
145+
* Convenience function for converting the final count to bits, assuming
146+
* that the counter is tracking bytes of input (its intended purpose).
147+
* */
148+
public fun asBits(): Final {
149+
if (isBits) return this
150+
return Final(lo shl 3, (hi shl 3) or (lo ushr 29), isBits = true)
151+
}
152+
153+
/** @suppress */
154+
public override fun equals(other: Any?): Boolean = other is Final && other.hashCode() == hashCode()
155+
/** @suppress */
156+
public override fun hashCode(): Int {
157+
var result = 17
158+
result = result * 31 + lo.hashCode()
159+
result = result * 31 + hi.hashCode()
160+
result = result * 31 + isBits.hashCode()
161+
return result
162+
}
163+
/** @suppress */
164+
public override fun toString(): String = "Counter.Bit32.Final[lo=$lo, hi=$hi]"
165+
}
166+
112167
private constructor(other: Bit32): super() {
113168
this.incrementBy = other.incrementBy
114169
this.lo = other.lo
@@ -128,7 +183,7 @@ public sealed class Counter private constructor(): Resettable, Copyable<Counter>
128183
/**
129184
* The maximum value for which [Bit64.incrementBy] can be set to.
130185
*
131-
* 1024^4 >> 1099511627776
186+
* 1024^4 = 1099511627776
132187
* */
133188
public const val MAX_INCREMENT: Long = 1099511627776L // Never decrease, only increase.
134189
}
@@ -190,11 +245,66 @@ public sealed class Counter private constructor(): Resettable, Copyable<Counter>
190245
if (lo == 0L) hi++
191246
}
192247

248+
/**
249+
* Produces a final count, including any additional value needed to be
250+
* added (such as the size of buffered input).
251+
*
252+
* **NOTE:** [reset] is not called and the [Counter] is unaltered by
253+
* [additional] value.
254+
* */
255+
public fun final(additional: Int): Final {
256+
var lo = lo
257+
var hi = hi
258+
val lt0 = lo < 0
259+
lo += additional
260+
if (lt0 && lo >= 0) hi++
261+
return Final(lo = lo, hi = hi)
262+
}
263+
193264
public override fun reset() {
194265
lo = 0L
195266
hi = 0L
196267
}
197268

269+
/**
270+
* Holder of the count produced by [final]
271+
* */
272+
public class Final private constructor(
273+
@JvmField
274+
public val lo: Long,
275+
@JvmField
276+
public val hi: Long,
277+
private val isBits: Boolean,
278+
) {
279+
280+
internal constructor(lo: Long, hi: Long): this(lo, hi, isBits = false)
281+
282+
public operator fun component1(): Long = lo
283+
public operator fun component2(): Long = hi
284+
285+
/**
286+
* Convenience function for converting the final count to bits, assuming
287+
* that the counter is tracking bytes of input (its intended purpose).
288+
* */
289+
public fun asBits(): Final {
290+
if (isBits) return this
291+
return Final(lo shl 3, (hi shl 3) or (lo ushr 29), isBits = true)
292+
}
293+
294+
/** @suppress */
295+
public override fun equals(other: Any?): Boolean = other is Final && other.hashCode() == hashCode()
296+
/** @suppress */
297+
public override fun hashCode(): Int {
298+
var result = 17
299+
result = result * 31 + lo.hashCode()
300+
result = result * 31 + hi.hashCode()
301+
result = result * 31 + isBits.hashCode()
302+
return result
303+
}
304+
/** @suppress */
305+
public override fun toString(): String = "Counter.Bit64.Final[lo=$lo, hi=$hi]"
306+
}
307+
198308
private constructor(other: Bit64): super() {
199309
this.incrementBy = other.incrementBy
200310
this.lo = other.lo

library/core/src/commonTest/kotlin/org/kotlincrypto/core/CounterUnitTest.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,70 @@ class CounterUnitTest {
143143
copy.increment()
144144
assertNotEquals(expected.lo, copy.lo)
145145
}
146+
147+
@Test
148+
fun givenBit32_whenFinal_thenAdditionalValueIsAsExpected() {
149+
val counter = Counter.Bit32(-8, 0, 8)
150+
151+
val (lo1, hi1) = counter.final(additional = 9)
152+
assertEquals(1, lo1)
153+
assertEquals(1, hi1)
154+
155+
val (lo2, hi2) = counter.final(additional = 8)
156+
assertEquals(0, lo2)
157+
assertEquals(1, hi2)
158+
159+
val (lo3, hi3) = counter.final(additional = 7)
160+
assertEquals(-1, lo3)
161+
assertEquals(0, hi3)
162+
}
163+
164+
@Test
165+
fun givenBit64_whenFinal_thenAdditionalValueIsAsExpected() {
166+
val counter = Counter.Bit64(-8, 0, 8)
167+
168+
val (lo1, hi1) = counter.final(additional = 9)
169+
assertEquals(1, lo1)
170+
assertEquals(1, hi1)
171+
172+
val (lo2, hi2) = counter.final(additional = 8)
173+
assertEquals(0, lo2)
174+
assertEquals(1, hi2)
175+
176+
val (lo3, hi3) = counter.final(additional = 7)
177+
assertEquals(-1, lo3)
178+
assertEquals(0, hi3)
179+
}
180+
181+
@Test
182+
fun givenBit32Final_whenAsBits_thenConvertsAsExpected() {
183+
val number = 5551889119L
184+
val final = Counter.Bit32.Final(lo = number.toInt(), hi = number.rotateLeft(32).toInt())
185+
val bits = final.asBits()
186+
assertNotEquals(final, bits)
187+
188+
// Should return same instance (already bits)
189+
assertEquals(bits, bits.asBits())
190+
191+
val expected = number * Byte.SIZE_BITS
192+
val actual = ((bits.hi.toLong() and 0xffffffff) shl 32) or (bits.lo.toLong() and 0xffffffff)
193+
assertEquals(expected, actual)
194+
}
195+
196+
@Test
197+
fun givenBit64Final_whenAsBits_thenConvertsAsExpected() {
198+
val number = 5551889119L
199+
val final = Counter.Bit64.Final(lo = number, hi = 1)
200+
val bits = final.asBits()
201+
assertNotEquals(final, bits)
202+
203+
// Should return same instance (already bits)
204+
assertEquals(bits, bits.asBits())
205+
206+
var expected = Long.MAX_VALUE
207+
expected += number + 1
208+
expected *= Byte.SIZE_BITS
209+
val actual = ((bits.hi and 0xffffffff) shl 32) or (bits.lo and 0xffffffff)
210+
assertEquals(expected, actual)
211+
}
146212
}

0 commit comments

Comments
 (0)