Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -518,14 +518,14 @@ class Proto3WireProtocCompatibilityTests {
val googleMessage = PizzaOuterClass.PizzaDelivery.newBuilder()
.setOrderedAt(
Timestamp.newBuilder()
.setSeconds(-631152000000L) // 1950-01-01T00:00:00.250Z.
.setSeconds(-631152000L) // 1950-01-01T00:00:00.250Z.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we were passing ms... instead of s !

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interestingly, protoc didn't throw though?

.setNanos(250_000_000)
.build(),
)
.build()

val wireMessage = PizzaDeliveryK(
ordered_at = ofEpochSecond(-631152000000L, 250_000_000L),
ordered_at = ofEpochSecond(-631152000L, 250_000_000L),
)

val googleMessageBytes = googleMessage.toByteArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ expect class Instant {
*
* For example, this value will be -1 for the instant 1969-12-31T23:59:59Z, and 1 for the instant
* 1970-01-01T00:00:01Z.
*
* It must be between -62135596800 and 253402300799 inclusive (which corresponds to
* 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z).
*/
fun getEpochSecond(): Long

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1277,32 +1277,49 @@ internal fun commonDuration(): ProtoAdapter<Duration> = object : ProtoAdapter<Du
}
}

// Protobuf Timestamp valid range: 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z inclusive.
private const val TIMESTAMP_SECONDS_MIN = -62135596800L
private const val TIMESTAMP_SECONDS_MAX = 253402300799L

@Suppress("NOTHING_TO_INLINE")
private inline fun checkTimestampRange(seconds: Long, nanos: Int) {
require(seconds in TIMESTAMP_SECONDS_MIN..TIMESTAMP_SECONDS_MAX) {
"Timestamp seconds ($seconds) must be in range [$TIMESTAMP_SECONDS_MIN, $TIMESTAMP_SECONDS_MAX]"
}
require(nanos in 0..999_999_999) {
"Timestamp nanos ($nanos) must be in range [0, 999999999]"
}
}

internal fun commonInstant(): ProtoAdapter<Instant> = object : ProtoAdapter<Instant>(
LENGTH_DELIMITED,
Instant::class,
"type.googleapis.com/google.protobuf.Timestamp",
Syntax.PROTO_3,
) {
override fun encodedSize(value: Instant): Int {
var result = 0
val seconds = value.getEpochSecond()
if (seconds != 0L) result += INT64.encodedSizeWithTag(1, seconds)
val nanos = value.getNano()
checkTimestampRange(seconds, nanos)
var result = 0
if (seconds != 0L) result += INT64.encodedSizeWithTag(1, seconds)
if (nanos != 0) result += INT32.encodedSizeWithTag(2, nanos)
return result
}

override fun encode(writer: ProtoWriter, value: Instant) {
val seconds = value.getEpochSecond()
if (seconds != 0L) INT64.encodeWithTag(writer, 1, seconds)
val nanos = value.getNano()
checkTimestampRange(seconds, nanos)
if (seconds != 0L) INT64.encodeWithTag(writer, 1, seconds)
if (nanos != 0) INT32.encodeWithTag(writer, 2, nanos)
}

override fun encode(writer: ReverseProtoWriter, value: Instant) {
val seconds = value.getEpochSecond()
val nanos = value.getNano()
checkTimestampRange(seconds, nanos)
if (nanos != 0) INT32.encodeWithTag(writer, 2, nanos)
val seconds = value.getEpochSecond()
if (seconds != 0L) INT64.encodeWithTag(writer, 1, seconds)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package com.squareup.wire

import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import kotlin.test.Test

Expand Down Expand Up @@ -44,4 +46,44 @@ class ProtoAdapterTest {
ProtoAdapter.BOOL.asPacked().asRepeated()
}.isInstanceOf<UnsupportedOperationException>()
}

@Test fun instantEncodeValidMinBoundary() {
// 0001-01-01T00:00:00Z.
val instant = ofEpochSecond(-62135596800L, 0L)
val bytes = ProtoAdapter.INSTANT.encode(instant)
assertThat(ProtoAdapter.INSTANT.decode(bytes).getEpochSecond()).isEqualTo(-62135596800L)
}

@Test fun instantEncodeValidMaxBoundary() {
// 9999-12-31T23:59:59Z.
val instant = ofEpochSecond(253402300799L, 999_999_999L)
val bytes = ProtoAdapter.INSTANT.encode(instant)
val decoded = ProtoAdapter.INSTANT.decode(bytes)
assertThat(decoded.getEpochSecond()).isEqualTo(253402300799L)
assertThat(decoded.getNano()).isEqualTo(999_999_999)
}

@Test fun instantEncodeRejectsSecondsBelowMin() {
// 0001-01-01T00:00:00Z - 1 second.
val instant = ofEpochSecond(-62135596801L, 0L)
assertFailure {
ProtoAdapter.INSTANT.encode(instant)
}.isInstanceOf<IllegalArgumentException>()
}

@Test fun instantEncodeRejectsSecondsAboveMax() {
// 9999-12-31T23:59:59Z + 1 second.
val instant = ofEpochSecond(253402300800L, 0L)
assertFailure {
ProtoAdapter.INSTANT.encode(instant)
}.isInstanceOf<IllegalArgumentException>()
}

@Test fun instantEncodedSizeRejectsOutOfRange() {
// 0001-01-01T00:00:00Z - 1 second.
val instant = ofEpochSecond(-62135596801L, 0L)
assertFailure {
ProtoAdapter.INSTANT.encodedSize(instant)
}.isInstanceOf<IllegalArgumentException>()
}
}
Loading