diff --git a/core/src/main/scala/proteus/ProtobufCodec.scala b/core/src/main/scala/proteus/ProtobufCodec.scala index edc8122..141a2f0 100644 --- a/core/src/main/scala/proteus/ProtobufCodec.scala +++ b/core/src/main/scala/proteus/ProtobufCodec.scala @@ -365,12 +365,12 @@ object ProtobufCodec { else CodedOutputStream.computeStringSize(id, value) case _: PrimitiveType.Double => val value = register.asInstanceOf[Register.Double].get(registers, offset) - if (value == 0d && !alwaysEncode) 0 + if (value == 0d && java.lang.Double.doubleToRawLongBits(value) == 0L && !alwaysEncode) 0 else if (id == -1) CodedOutputStream.computeDoubleSizeNoTag(value) else CodedOutputStream.computeDoubleSize(id, value) case _: PrimitiveType.Float => val value = register.asInstanceOf[Register.Float].get(registers, offset) - if (value == 0f && !alwaysEncode) 0 + if (value == 0f && java.lang.Float.floatToRawIntBits(value) == 0 && !alwaysEncode) 0 else if (id == -1) CodedOutputStream.computeFloatSizeNoTag(value) else CodedOutputStream.computeFloatSize(id, value) case _ => throw new ProteusException(s"Unsupported primitive type: $primitiveType") @@ -400,12 +400,12 @@ object ProtobufCodec { else CodedOutputStream.computeStringSize(id, value) case _: PrimitiveType.Double => val value: Double = a - if (value == 0d && !alwaysEncode) 0 + if (value == 0d && java.lang.Double.doubleToRawLongBits(value) == 0L && !alwaysEncode) 0 else if (id == -1) CodedOutputStream.computeDoubleSizeNoTag(value) else CodedOutputStream.computeDoubleSize(id, value) case _: PrimitiveType.Float => val value: Float = a - if (value == 0f && !alwaysEncode) 0 + if (value == 0f && java.lang.Float.floatToRawIntBits(value) == 0 && !alwaysEncode) 0 else if (id == -1) CodedOutputStream.computeFloatSizeNoTag(value) else CodedOutputStream.computeFloatSize(id, value) case _ => throw new ProteusException(s"Unsupported primitive type: $primitiveType") @@ -437,12 +437,12 @@ object ProtobufCodec { } case _: PrimitiveType.Double => val value = register.asInstanceOf[Register.Double].get(registers, offset) - if (value != 0d || alwaysEncode) { + if (value != 0d || java.lang.Double.doubleToRawLongBits(value) != 0L || alwaysEncode) { if (id == -1) output.writeDoubleNoTag(value) else output.writeDouble(id, value) } case _: PrimitiveType.Float => val value = register.asInstanceOf[Register.Float].get(registers, offset) - if (value != 0f || alwaysEncode) { + if (value != 0f || java.lang.Float.floatToRawIntBits(value) != 0 || alwaysEncode) { if (id == -1) output.writeFloatNoTag(value) else output.writeFloat(id, value) } case _ => throw new ProteusException(s"Unsupported primitive type: $primitiveType") @@ -472,12 +472,12 @@ object ProtobufCodec { } case _: PrimitiveType.Double => val value: Double = a - if (value != 0d || alwaysEncode) { + if (value != 0d || java.lang.Double.doubleToRawLongBits(value) != 0L || alwaysEncode) { if (id == -1) output.writeDoubleNoTag(value) else output.writeDouble(id, value) } case _: PrimitiveType.Float => val value: Float = a - if (value != 0f || alwaysEncode) { + if (value != 0f || java.lang.Float.floatToRawIntBits(value) != 0 || alwaysEncode) { if (id == -1) output.writeFloatNoTag(value) else output.writeFloat(id, value) } case _ => throw new ProteusException(s"Unsupported primitive type: $primitiveType") diff --git a/core/src/main/scala/proteus/ProtobufDeriver.scala b/core/src/main/scala/proteus/ProtobufDeriver.scala index c31033f..a02ab2a 100644 --- a/core/src/main/scala/proteus/ProtobufDeriver.scala +++ b/core/src/main/scala/proteus/ProtobufDeriver.scala @@ -271,7 +271,7 @@ case class ProtobufDeriver private ( getComment(field.modifiers) ) case instance => - val fieldId = getReservedIndex(field.modifiers) match { + val fieldId = getReservedIndex(field.modifiers) match { case Some(reservedIndex) => reservedIndex case None => getReservedFromIndex(field.modifiers) match { @@ -284,12 +284,16 @@ case class ProtobufDeriver private ( id } } + val fieldDefault = instance match { + case _: ProtobufCodec.Primitive[?] => getDefaultValue(using instance) + case _ => field.value.getDefaultValue.getOrElse(getDefaultValue(using instance)) + } builder += SimpleField( name, fieldId, instance, register, - field.value.getDefaultValue.getOrElse(getDefaultValue(using instance)), + fieldDefault, getComment(field.modifiers), isDeprecated(field.modifiers) ) diff --git a/core/src/test/scala/proteus/ProtobufCodecSpec.scala b/core/src/test/scala/proteus/ProtobufCodecSpec.scala index 6c4e752..f798856 100644 --- a/core/src/test/scala/proteus/ProtobufCodecSpec.scala +++ b/core/src/test/scala/proteus/ProtobufCodecSpec.scala @@ -75,6 +75,25 @@ object ProtobufCodecSpec extends ZIOSpecDefault { val decoded = codec.decode(encoded) assert(decoded)(equalTo(original)) + }, + test("primitive field with a non-zero default round-trips its zero value") { + case class WithDefault(count: Int = 5, label: String = "x") derives Schema + val codec = Schema[WithDefault].derive(deriver) + + val original = WithDefault(0, "") + val decoded = codec.decode(codec.encode(original)) + + assert(decoded)(equalTo(original)) + }, + test("negative-zero double and float fields preserve their sign bit") { + case class Zeros(d: Double, f: Float) derives Schema + val codec = Schema[Zeros].derive(deriver) + + val original = Zeros(-0.0, -0.0f) + val decoded = codec.decode(codec.encode(original)) + + assert(java.lang.Double.doubleToRawLongBits(decoded.d))(equalTo(java.lang.Double.doubleToRawLongBits(-0.0))) && + assert(java.lang.Float.floatToRawIntBits(decoded.f))(equalTo(java.lang.Float.floatToRawIntBits(-0.0f))) } ), suite("Enum Tests")(