All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.com.squareup.wire.Message.kt Maven / Gradle / Ivy

/*
 * Copyright 2013 Square Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.wire

import okio.Buffer
import okio.BufferedSink
import okio.ByteString
import java.io.IOException
import java.io.ObjectStreamException
import java.io.OutputStream
import java.io.Serializable

/** A protocol buffer message. */
actual abstract class Message, B : Message.Builder>
protected actual constructor(
  /** The [ProtoAdapter] for encoding and decoding messages of this type. */
  @field:Transient @get:JvmName("adapter") actual val adapter: ProtoAdapter,
  unknownFields: ByteString
) : Serializable {
  /**
   * Returns a byte string containing the proto encoding of this message's unknown fields. Returns
   * an empty byte string if this message has no unknown fields.
   */
  @field:Transient @get:JvmName("unknownFields")
  actual val unknownFields: ByteString = unknownFields
    get() {
      // Some naughty libraries construct Messages by reflection which causes this non-null field
      // to have a null value. We defend against this with an otherwise-redundant null check.
      @Suppress("SENSELESS_COMPARISON")
      if (field == null) return ByteString.EMPTY
      return field
    }

  /** If not `0` then the serialized size of this message. */
  @Transient internal var cachedSerializedSize = 0

  /** If non-zero, the hash code of this message. Accessed by generated code. */
  @Transient @JvmField protected actual var hashCode = 0

  /**
   * Returns a new builder initialized with the data in this message.
   */
  actual abstract fun newBuilder(): B

  /** Returns this message with any unknown fields removed. */
  fun withoutUnknownFields(): M = newBuilder().clearUnknownFields().build()

  override fun toString(): String = adapter.toString(this as M)

  @Throws(ObjectStreamException::class)
  protected fun writeReplace(): Any = MessageSerializedForm(encode(), javaClass as Class)

  /** Encode this message and write it to `stream`. */
  @Throws(IOException::class)
  actual fun encode(sink: BufferedSink) {
    adapter.encode(sink, this as M)
  }

  /** Encode this message as a `byte[]`.  */
  actual fun encode(): ByteArray = adapter.encode(this as M)

  /** Encode this message as a `ByteString`. */
  actual fun encodeByteString(): ByteString {
    return adapter.encodeByteString(this as M)
  }


  /** Encode this message and write it to `stream`.  */
  @Throws(IOException::class)
  fun encode(stream: OutputStream) {
    adapter.encode(stream, this as M)
  }

  /**
   * Superclass for protocol buffer message builders.
   */
  actual abstract class Builder, B : Builder> protected constructor() {
    /**
     * Caches unknown fields as a [ByteString] when [buildUnknownFields] is called.
     * When the caller adds an additional unknown field after that, it will be written to the new
     * [unknownFieldsBuffer] to ensure that all unknown fields are retained between calls to
     * [buildUnknownFields].
     */
    @Transient internal var unknownFieldsByteString = ByteString.EMPTY
    /**
     * [Buffer] of the message's unknown fields that is lazily instantiated between calls to
     * [buildUnknownFields]. It's automatically cleared in [buildUnknownFields], and can also be
     * manually cleared by calling [clearUnknownFields].
     */
    @Transient internal var unknownFieldsBuffer: Buffer? = null
    @Transient internal var unknownFieldsWriter: ProtoWriter? = null

    fun addUnknownFields(unknownFields: ByteString): Builder = apply {
      if (unknownFields.size > 0) {
        prepareForNewUnknownFields()
        unknownFieldsWriter!!.writeBytes(unknownFields)
      }
    }

    fun addUnknownField(
      tag: Int,
      fieldEncoding: FieldEncoding,
      value: Any?
    ): Builder = apply {
      prepareForNewUnknownFields()
      val protoAdapter = fieldEncoding.rawProtoAdapter() as ProtoAdapter
      protoAdapter.encodeWithTag(unknownFieldsWriter!!, tag, value)
    }

    fun clearUnknownFields(): Builder = apply {
      unknownFieldsByteString = ByteString.EMPTY
      if (unknownFieldsBuffer != null) {
        unknownFieldsBuffer!!.clear()
        unknownFieldsBuffer = null
      }
      unknownFieldsWriter = null
    }

    /**
     * Returns a byte string with this message's unknown fields. Returns an empty byte string if
     * this message has no unknown fields.
     */
    fun buildUnknownFields(): ByteString {
      if (unknownFieldsBuffer != null) {
        // Reads and caches the unknown fields from the buffer.
        unknownFieldsByteString = unknownFieldsBuffer!!.readByteString()
        unknownFieldsBuffer = null
        unknownFieldsWriter = null
      }
      return unknownFieldsByteString
    }

    /** Returns an immutable [Message] based on the fields that set in this builder. */
    abstract fun build(): M

    private fun prepareForNewUnknownFields() {
      if (unknownFieldsBuffer == null) {
        unknownFieldsBuffer = Buffer()
        unknownFieldsWriter = ProtoWriter(unknownFieldsBuffer!!)
        // Writes the cached unknown fields to the buffer.
        unknownFieldsWriter!!.writeBytes(unknownFieldsByteString)
        unknownFieldsByteString = ByteString.EMPTY
      }
    }
  }

  companion object {
    private const val serialVersionUID = 0L
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy