Skip to content

Commit 9cc5553

Browse files
committed
Added API for reading and writing of floating point numbers
Implemented extension functions for reading and writing values with types Float and Double Resolves #167
1 parent cbeb279 commit 9cc5553

File tree

6 files changed

+277
-0
lines changed

6 files changed

+277
-0
lines changed

core/api/kotlinx-io-core.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ public final class kotlinx/io/SinksJvmKt {
113113

114114
public final class kotlinx/io/SinksKt {
115115
public static final fun writeDecimalLong (Lkotlinx/io/Sink;J)V
116+
public static final fun writeDouble (Lkotlinx/io/Sink;D)V
117+
public static final fun writeDoubleLe (Lkotlinx/io/Sink;D)V
118+
public static final fun writeFloat (Lkotlinx/io/Sink;F)V
119+
public static final fun writeFloatLe (Lkotlinx/io/Sink;F)V
116120
public static final fun writeHexadecimalUnsignedLong (Lkotlinx/io/Sink;J)V
117121
public static final fun writeIntLe (Lkotlinx/io/Sink;I)V
118122
public static final fun writeLongLe (Lkotlinx/io/Sink;J)V
@@ -158,6 +162,10 @@ public final class kotlinx/io/SourcesKt {
158162
public static final fun readByteArray (Lkotlinx/io/Source;)[B
159163
public static final fun readByteArray (Lkotlinx/io/Source;I)[B
160164
public static final fun readDecimalLong (Lkotlinx/io/Source;)J
165+
public static final fun readDouble (Lkotlinx/io/Source;)D
166+
public static final fun readDoubleLe (Lkotlinx/io/Source;)D
167+
public static final fun readFloat (Lkotlinx/io/Source;)F
168+
public static final fun readFloatLe (Lkotlinx/io/Source;)F
161169
public static final fun readHexadecimalUnsignedLong (Lkotlinx/io/Source;)J
162170
public static final fun readIntLe (Lkotlinx/io/Source;)I
163171
public static final fun readLongLe (Lkotlinx/io/Source;)J

core/common/src/Sinks.kt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,70 @@ public fun Sink.writeULongLe(long: ULong) {
275275
writeLongLe(long.toLong())
276276
}
277277

278+
/**
279+
* Writes four bytes of a bit representation of [float], in the big-endian order, to this sink.
280+
* Bit representation of the [float] corresponds to the IEEE 754 floating-point "single format" bit layout.
281+
*
282+
* To obtain a bit representation, the [Float.toBits] function is used.
283+
*
284+
* @param float the floating point number to be written.
285+
*
286+
* @throws IllegalStateException when the sink is closed.
287+
*
288+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeFloat
289+
*/
290+
public fun Sink.writeFloat(float: Float) {
291+
writeInt(float.toBits())
292+
}
293+
294+
/**
295+
* Writes eight bytes of a bit representation of [double], in the big-endian order, to this sink.
296+
* Bit representation of the [double] corresponds to the IEEE 754 floating-point "double format" bit layout.
297+
*
298+
* To obtain a bit representation, the [Double.toBits] function is used.
299+
*
300+
* @param double the floating point number to be written.
301+
*
302+
* @throws IllegalStateException when the sink is closed.
303+
*
304+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDouble
305+
*/
306+
public fun Sink.writeDouble(double: Double) {
307+
writeLong(double.toBits())
308+
}
309+
310+
/**
311+
* Writes four bytes of a bit representation of [float], in the little-endian order, to this sink.
312+
* Bit representation of the [float] corresponds to the IEEE 754 floating-point "single format" bit layout.
313+
*
314+
* To obtain a bit representation, the [Float.toBits] function is used.
315+
*
316+
* @param float the floating point number to be written.
317+
*
318+
* @throws IllegalStateException when the sink is closed.
319+
*
320+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeFloatLe
321+
*/
322+
public fun Sink.writeFloatLe(float: Float) {
323+
writeIntLe(float.toBits())
324+
}
325+
326+
/**
327+
* Writes eight bytes of a bit representation of [double], in the little-endian order, to this sink.
328+
* Bit representation of the [double] corresponds to the IEEE 754 floating-point "double format" bit layout.
329+
*
330+
* To obtain a bit representation, the [Double.toBits] function is used.
331+
*
332+
* @param double the floating point number to be written.
333+
*
334+
* @throws IllegalStateException when the sink is closed.
335+
*
336+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDoubleLe
337+
*/
338+
public fun Sink.writeDoubleLe(double: Double) {
339+
writeLongLe(double.toBits())
340+
}
341+
278342
/**
279343
* Provides direct access to the sink's internal buffer and hints its emit before exit.
280344
*

core/common/src/Sources.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,58 @@ public fun Source.readUIntLe(): UInt = readIntLe().toUInt()
357357
*/
358358
public fun Source.readULongLe(): ULong = readLongLe().toULong()
359359

360+
/**
361+
* Removes four bytes from this source and returns a floating point number with type [Float] composed of it
362+
* according to the big-endian order.
363+
*
364+
* The [Float.Companion.fromBits] function is used for decoding bytes into [Float].
365+
*
366+
* @throws EOFException when there are not enough data to read an unsigned int value.
367+
* @throws IllegalStateException when the source is closed.
368+
*
369+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readFloat
370+
*/
371+
public fun Source.readFloat(): Float = Float.fromBits(readInt())
372+
373+
/**
374+
* Removes eight bytes from this source and returns a floating point number with type [Double] composed of it
375+
* according to the big-endian order.
376+
*
377+
* The [Double.Companion.fromBits] function is used for decoding bytes into [Double].
378+
*
379+
* @throws EOFException when there are not enough data to read an unsigned int value.
380+
* @throws IllegalStateException when the source is closed.
381+
*
382+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDouble
383+
*/
384+
public fun Source.readDouble(): Double = Double.fromBits(readLong())
385+
386+
/**
387+
* Removes four bytes from this source and returns a floating point number with type [Float] composed of it
388+
* according to the little-endian order.
389+
*
390+
* The [Float.Companion.fromBits] function is used for decoding bytes into [Float].
391+
*
392+
* @throws EOFException when there are not enough data to read an unsigned int value.
393+
* @throws IllegalStateException when the source is closed.
394+
*
395+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readFloatLe
396+
*/
397+
public fun Source.readFloatLe(): Float = Float.fromBits(readIntLe())
398+
399+
/**
400+
* Removes eight bytes from this source and returns a floating point number with type [Double] composed of it
401+
* according to the little-endian order.
402+
*
403+
* The [Double.Companion.fromBits] function is used for decoding bytes into [Double].
404+
*
405+
* @throws EOFException when there are not enough data to read an unsigned int value.
406+
* @throws IllegalStateException when the source is closed.
407+
*
408+
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDoubleLe
409+
*/
410+
public fun Source.readDoubleLe(): Double = Double.fromBits(readLongLe())
411+
360412
/**
361413
* Return `true` if the next byte to be consumed from this source is equal to [byte].
362414
* Otherwise, return `false` as well as when the source is exhausted.

core/common/test/AbstractSinkTest.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,34 @@ abstract class AbstractSinkTest internal constructor(
449449
assertEquals("Buffer(size=8 hex=efcdab9078563412)", data.toString())
450450
}
451451

452+
@Test
453+
fun writeFloat() {
454+
sink.writeFloat(12345.678F)
455+
sink.flush()
456+
assertEquals(12345.678F.toBits(), data.readInt())
457+
}
458+
459+
@Test
460+
fun writeFloatLe() {
461+
sink.writeFloatLe(12345.678F)
462+
sink.flush()
463+
assertEquals(12345.678F.toBits(), data.readIntLe())
464+
}
465+
466+
@Test
467+
fun writeDouble() {
468+
sink.writeDouble(123456.78901)
469+
sink.flush()
470+
assertEquals(123456.78901.toBits(), data.readLong())
471+
}
472+
473+
@Test
474+
fun writeDoubleLe() {
475+
sink.writeDoubleLe(123456.78901)
476+
sink.flush()
477+
assertEquals(123456.78901.toBits(), data.readLongLe())
478+
}
479+
452480
@Test
453481
fun writeByteString() {
454482
sink.write("təˈranəˌsôr".encodeToByteString())

core/common/test/AbstractSourceTest.kt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,70 @@ abstract class AbstractBufferedSourceTest internal constructor(
14641464
assertEquals(0x78563412u, source.readUIntLe())
14651465
}
14661466

1467+
@Test
1468+
fun readFloat() {
1469+
sink.write(byteArrayOf(70, 64, -26, -74))
1470+
sink.flush()
1471+
assertEquals(12345.678F.toBits(), source.readFloat().toBits())
1472+
}
1473+
1474+
@Test
1475+
fun readDouble() {
1476+
sink.write(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35))
1477+
sink.flush()
1478+
assertEquals(123456.78901, source.readDouble())
1479+
}
1480+
1481+
@Test
1482+
fun readFloatLe() {
1483+
sink.write(byteArrayOf(-74, -26, 64, 70))
1484+
sink.flush()
1485+
assertEquals(12345.678F.toBits(), source.readFloatLe().toBits())
1486+
}
1487+
1488+
@Test
1489+
fun readDoubleLe() {
1490+
sink.write(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64))
1491+
sink.flush()
1492+
assertEquals(123456.78901, source.readDoubleLe())
1493+
}
1494+
1495+
@Test
1496+
fun readTooShortFloatThrows() {
1497+
assertFailsWith<EOFException> { source.readFloat() }
1498+
sink.writeByte(0)
1499+
sink.flush()
1500+
assertFailsWith<EOFException> { source.readFloat() }
1501+
assertTrue(source.request(1))
1502+
}
1503+
1504+
@Test
1505+
fun readTooShortDoubleThrows() {
1506+
assertFailsWith<EOFException> { source.readDouble() }
1507+
sink.writeByte(0)
1508+
sink.flush()
1509+
assertFailsWith<EOFException> { source.readDouble() }
1510+
assertTrue(source.request(1))
1511+
}
1512+
1513+
@Test
1514+
fun readTooShortFloatLeThrows() {
1515+
assertFailsWith<EOFException> { source.readFloatLe() }
1516+
sink.writeByte(0)
1517+
sink.flush()
1518+
assertFailsWith<EOFException> { source.readFloatLe() }
1519+
assertTrue(source.request(1))
1520+
}
1521+
1522+
@Test
1523+
fun readTooShortDoubleLeThrows() {
1524+
assertFailsWith<EOFException> { source.readDoubleLe() }
1525+
sink.writeByte(0)
1526+
sink.flush()
1527+
assertFailsWith<EOFException> { source.readDoubleLe() }
1528+
assertTrue(source.request(1))
1529+
}
1530+
14671531
@Test
14681532
fun readTooShortUnsignedIntThrows() {
14691533
assertFailsWith<EOFException> { source.readUInt() }

core/common/test/samples/samples.kt

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,21 @@ class KotlinxIoCoreCommonSamples {
488488
assertEquals(18446744073709551615UL, buffer.readULong())
489489
}
490490

491+
@Test
492+
fun readFloat() {
493+
val buffer = Buffer()
494+
buffer.write(byteArrayOf(70, 64, -26, -74))
495+
assertEquals(12345.678F.toBits(), buffer.readFloat().toBits())
496+
}
497+
498+
@Test
499+
fun readDouble() {
500+
val buffer = Buffer()
501+
buffer.write(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35))
502+
503+
assertEquals(123456.78901, buffer.readDouble())
504+
}
505+
491506
@Test
492507
fun writeUByte() {
493508
val buffer = Buffer()
@@ -520,6 +535,22 @@ class KotlinxIoCoreCommonSamples {
520535
assertContentEquals(byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1), buffer.readByteArray())
521536
}
522537

538+
@Test
539+
fun writeFloat() {
540+
val buffer = Buffer()
541+
buffer.writeFloat(12345.678F)
542+
543+
assertContentEquals(byteArrayOf(70, 64, -26, -74), buffer.readByteArray())
544+
}
545+
546+
@Test
547+
fun writeDouble() {
548+
val buffer = Buffer()
549+
buffer.writeDouble(123456.78901)
550+
551+
assertContentEquals(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35), buffer.readByteArray())
552+
}
553+
523554
@Test
524555
fun flush() {
525556
val rawSink = object : RawSink {
@@ -650,6 +681,20 @@ class KotlinxIoCoreCommonSamples {
650681
assertEquals(0xF0DEBC9A78563412U, buffer.readULongLe())
651682
}
652683

684+
@Test
685+
fun readFloatLe() {
686+
val buffer = Buffer()
687+
buffer.write(byteArrayOf(-74, -26, 64, 70))
688+
assertEquals(12345.678F.toBits(), buffer.readFloatLe().toBits())
689+
}
690+
691+
@Test
692+
fun readDoubleLe() {
693+
val buffer = Buffer()
694+
buffer.write(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64))
695+
assertEquals(123456.78901, buffer.readDoubleLe())
696+
}
697+
653698
@Test
654699
fun writeUShortLe() {
655700
val buffer = Buffer()
@@ -670,4 +715,20 @@ class KotlinxIoCoreCommonSamples {
670715
buffer.writeULongLe(0x123456789ABCDEF0U)
671716
assertEquals(0xF0DEBC9A78563412U, buffer.readULong())
672717
}
718+
719+
@Test
720+
fun writeFloatLe() {
721+
val buffer = Buffer()
722+
buffer.writeFloatLe(12345.678F)
723+
724+
assertContentEquals(byteArrayOf(-74, -26, 64, 70), buffer.readByteArray())
725+
}
726+
727+
@Test
728+
fun writeDoubleLe() {
729+
val buffer = Buffer()
730+
buffer.writeDoubleLe(123456.78901)
731+
732+
assertContentEquals(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64), buffer.readByteArray())
733+
}
673734
}

0 commit comments

Comments
 (0)