Skip to content

Commit fcd1943

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 fcd1943

File tree

5 files changed

+269
-0
lines changed

5 files changed

+269
-0
lines changed

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)