From 2a1a8af1f6a7393fdd66f0d7ca3a10edd8bdc1f3 Mon Sep 17 00:00:00 2001 From: Jakub Sobolewski Date: Wed, 10 Jul 2024 13:37:16 +0200 Subject: [PATCH] GH-1630: Fix TlvEncoder - date (TIME) should be encoded like integer. --- .../eclipse/leshan/core/tlv/TlvEncoder.java | 17 ++++- .../leshan/core/tlv/TlvEncoderTest.java | 73 +++++++++++++++++-- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/leshan-lwm2m-core/src/main/java/org/eclipse/leshan/core/tlv/TlvEncoder.java b/leshan-lwm2m-core/src/main/java/org/eclipse/leshan/core/tlv/TlvEncoder.java index 1b3f4b77dd..90e5e93bff 100644 --- a/leshan-lwm2m-core/src/main/java/org/eclipse/leshan/core/tlv/TlvEncoder.java +++ b/leshan-lwm2m-core/src/main/java/org/eclipse/leshan/core/tlv/TlvEncoder.java @@ -113,8 +113,21 @@ public static byte[] encodeString(String value) { * Encodes a date value. */ public static byte[] encodeDate(Date value) { - ByteBuffer tBuf = ByteBuffer.allocate(4); - tBuf.putInt((int) (value.getTime() / 1000L)); + ByteBuffer tBuf; + long lValue = value.getTime() / 1000L; + if (lValue <= Byte.MAX_VALUE && lValue >= Byte.MIN_VALUE) { + tBuf = ByteBuffer.allocate(1); + tBuf.put((byte) lValue); + } else if (lValue <= Short.MAX_VALUE && lValue >= Short.MIN_VALUE) { + tBuf = ByteBuffer.allocate(2); + tBuf.putShort((short) lValue); + } else if (lValue <= Integer.MAX_VALUE && lValue >= Integer.MIN_VALUE) { + tBuf = ByteBuffer.allocate(4); + tBuf.putInt((int) lValue); + } else { + tBuf = ByteBuffer.allocate(8); + tBuf.putLong(lValue); + } return tBuf.array(); } diff --git a/leshan-lwm2m-core/src/test/java/org/eclipse/leshan/core/tlv/TlvEncoderTest.java b/leshan-lwm2m-core/src/test/java/org/eclipse/leshan/core/tlv/TlvEncoderTest.java index 944da7a38d..ac6c19b335 100644 --- a/leshan-lwm2m-core/src/test/java/org/eclipse/leshan/core/tlv/TlvEncoderTest.java +++ b/leshan-lwm2m-core/src/test/java/org/eclipse/leshan/core/tlv/TlvEncoderTest.java @@ -19,7 +19,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.nio.ByteBuffer; +import java.time.ZoneId; +import java.util.Calendar; import java.util.Date; +import java.util.TimeZone; import org.eclipse.leshan.core.tlv.Tlv.TlvType; import org.junit.jupiter.api.Test; @@ -57,15 +60,71 @@ public void encode_long() { assertEquals(0, bb.remaining()); } - @Test - public void encode_date() { - long timestamp = System.currentTimeMillis(); - byte[] encoded = TlvEncoder.encodeDate(new Date(timestamp)); + private void shouldEncodeDateAsNumberOfBytes(long utcTimeInSeconds, int expectedNumberOfBytes) throws TlvException { + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(ZoneId.of("UTC"))); + cal.setTimeInMillis(utcTimeInSeconds * 1000); - // check value + // encode + byte[] encoded = TlvEncoder.encodeDate(cal.getTime()); + + // check byte array value ByteBuffer bb = ByteBuffer.wrap(encoded); - assertEquals((int) (timestamp / 1000), bb.getInt()); - assertEquals(0, bb.remaining()); + if (expectedNumberOfBytes == 1) { + assertEquals((byte) (cal.getTimeInMillis() / 1000), bb.get()); + assertEquals(0, bb.remaining()); + } else if (expectedNumberOfBytes == 2) { + assertEquals((short) (cal.getTimeInMillis() / 1000), bb.getShort()); + assertEquals(0, bb.remaining()); + } else if (expectedNumberOfBytes == 4) { + assertEquals((int) (cal.getTimeInMillis() / 1000), bb.getInt()); + assertEquals(0, bb.remaining()); + } else { + assertEquals(cal.getTimeInMillis() / 1000, bb.getLong()); + assertEquals(0, bb.remaining()); + } + + // confirm decoded value + Date date = TlvDecoder.decodeDate(encoded); + assertEquals(cal.getTimeInMillis(), date.getTime()); + } + + @Test + public void encode_date_byte() throws TlvException { + shouldEncodeDateAsNumberOfBytes(Byte.MAX_VALUE, 1); + shouldEncodeDateAsNumberOfBytes(Byte.MIN_VALUE, 1); + shouldEncodeDateAsNumberOfBytes(0, 1); + shouldEncodeDateAsNumberOfBytes(100, 1); + shouldEncodeDateAsNumberOfBytes(-100, 1); + } + + @Test + public void encode_date_short() throws TlvException { + shouldEncodeDateAsNumberOfBytes(Byte.MAX_VALUE + 1, 2); + shouldEncodeDateAsNumberOfBytes(Byte.MIN_VALUE - 1, 2); + shouldEncodeDateAsNumberOfBytes(Short.MAX_VALUE, 2); + shouldEncodeDateAsNumberOfBytes(Short.MIN_VALUE, 2); + shouldEncodeDateAsNumberOfBytes(32000, 2); + shouldEncodeDateAsNumberOfBytes(-32000, 2); + } + + @Test + public void encode_date_int() throws TlvException { + shouldEncodeDateAsNumberOfBytes(Short.MAX_VALUE + 1, 4); + shouldEncodeDateAsNumberOfBytes(Short.MIN_VALUE - 1, 4); + shouldEncodeDateAsNumberOfBytes(Integer.MAX_VALUE, 4); + shouldEncodeDateAsNumberOfBytes(Integer.MIN_VALUE, 4); + shouldEncodeDateAsNumberOfBytes(Integer.MAX_VALUE - 100, 4); + shouldEncodeDateAsNumberOfBytes(Integer.MIN_VALUE + 100, 4); + } + + @Test + public void encode_date_long() throws TlvException { + shouldEncodeDateAsNumberOfBytes((long) Integer.MAX_VALUE + 1, 8); + shouldEncodeDateAsNumberOfBytes((long) Integer.MIN_VALUE - 1, 8); + shouldEncodeDateAsNumberOfBytes(Long.MAX_VALUE / 1000, 8); + shouldEncodeDateAsNumberOfBytes(Long.MIN_VALUE / 1000, 8); + shouldEncodeDateAsNumberOfBytes(Long.MAX_VALUE / 1000 - 100, 8); + shouldEncodeDateAsNumberOfBytes(Long.MIN_VALUE / 1000 + 100, 8); } @Test