-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds support for writing binary Ion 1.1 timestamps #618
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅ 📢 Thoughts on this report? Let us know! |
import com.amazon.ion.Decimal; | ||
import com.amazon.ion.IonType; | ||
import com.amazon.ion.Timestamp; | ||
import com.amazon.ion.impl.bin.utf8.Utf8StringEncoder; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These were unused imports.
@@ -72,7 +86,7 @@ public void testWriteNullValueForDatagram() { | |||
"true, 5E", | |||
"false, 5F", | |||
}) | |||
public void testWriteBooleanValue(Boolean value, String expectedBytes) { | |||
public void testWriteBooleanValue(boolean value, String expectedBytes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an incidental change. Then writeBooleanValue
method accepts boolean
, so I made the test do the same.
public void testWriteIntegerValueForBigInteger(BigInteger value, String expectedBytes) { | ||
assertWritingValue(expectedBytes, value, IonEncoder_1_1::writeIntValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also an incidental change since I realized that JUnit already knows how to convert to BigInteger
.
@@ -1453,17 +1453,40 @@ public static int fixedUIntLength(final long value) { | |||
*/ | |||
public int writeFixedUInt(final long value) { | |||
if (value < 0) { | |||
throw new IllegalArgumentException("Attempted to write a FlexUInt for " + value); | |||
throw new IllegalArgumentException("Attempted to write a FixedUInt for " + value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just fixing a typo that I noticed while working on Timestamps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still need to double-check the tests for you, but leaving these comments here for now.
} | ||
|
||
// Condition 2: The fractional seconds are a common precision. | ||
int secondsScale = value.getDecimalSecond().scale(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getDecimalSecond()
creates a new BigDecimal
on each call. Let's use getZFractionalSecond()
instead. It's deprecated, but if we ever get around to removing it we'll replace it with some internal accessor. Note: this also requires a null check.
|
||
// Condition 2: The fractional seconds are a common precision. | ||
int secondsScale = value.getDecimalSecond().scale(); | ||
if (secondsScale != 0 && secondsScale != 3 && secondsScale != 6 && secondsScale != 9) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: in my initial prototype I used secondsScale % 3 != 0 || secondsScale > 9
. Comparing these alternatives, I think it's pretty much a wash. In the common case (that the scale matches one of the common precisions) my suggestion always requires two comparisons, but it also has the modulo. Yours varies depending on the actual precision. One comparison if the scale is 0, two if it's 3, and so on. Leaving this comment in case you feel like forming a strong opinion on which one is preferable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a preference, and I suspect that it will not have very significant difference. I'll create an issue to revisit this later.
static final int L_TIMESTAMP_OFFSET_BIT_OFFSET = 34; | ||
static final int L_TIMESTAMP_SECOND_BIT_OFFSET = 44; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks off. The offset is 12 bits, but here the second bit is only 10 after the offset bits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.
|
||
bits |= ((long) value.getSecond()) << S_U_TIMESTAMP_SECOND_BIT_OFFSET; | ||
|
||
int secondsScale = value.getDecimalSecond().scale(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getZFractionalSecond()
here too, and below
|
||
int secondsScale = value.getDecimalSecond().scale(); | ||
if (secondsScale != 0) { | ||
long fractionalSeconds = value.getDecimalSecond().remainder(BigDecimal.ONE).movePointRight(secondsScale).longValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see value.unscaledValue().longValue()
in my prototype. Maybe you can get that to work. Remember that any BigDecimal
method that returns BigDecimal
allocates a new one, so we want to minimize that.
@ParameterizedTest | ||
@CsvSource({ | ||
// OpCode MYYYYYYY DDDDDMMM mmmHHHHH ooooommm ssssssoo ffffffff ffffffff ffffffff ..ffffff | ||
"2023-10-15T01:00-14:00, 01111000 00110101 01111101 00000001 01000000 00000010", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the offset bits all be zero for this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why yes, it should. 😅
I've gone and manually double checked several more test cases with offsets both in the short form and the long form timestamps. I forgot about adjusting the offset for the short form, but the long form looks okay.
Issue #, if available:
None
Description of changes:
Adds support for writing binary Ion 1.1 timestamps.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.