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

fm.common.Serializer.scala Maven / Gradle / Ivy

/*
 * Copyright 2014 Frugal Mechanic (http://frugalmechanic.com)
 *
 * 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 fm.common

import java.nio.charset.StandardCharsets.UTF_8
import java.util.Comparator

/**
 * This is here so that LazySeq can be split out into it's own project.
 * 
 * Some implementations are still in our internal Util package for now
 * until we get a chance to refactor and possible merge with our fm-serializer
 * project.
 */
trait Serializer[T] {
  def serialize(value: T): Array[Byte]
  def deserialize(bytes: Array[Byte]): T
}

/**
 * Implicits that go along with the Serializer trait
 */
object Serializer {
  implicit object StringSerializer extends Serializer[String] {
    final def serialize(value: String) = value.getBytes(UTF_8)
    final def deserialize(bytes: Array[Byte]) = new String(bytes, UTF_8)
  }
  
  implicit object ByteArraySerializer extends Serializer[Array[Byte]] {
    final def serialize(value: Array[Byte]) = value
    final def deserialize(bytes: Array[Byte]) = bytes
  }
  
  implicit object BooleanSerializer extends Serializer[Boolean] {
    final def serialize(value: Boolean) = Array[Byte](if(value) 1 else 0)
    final def deserialize(bytes: Array[Byte]) = if(bytes(0) == 0) false else true
  }
  
  // TODO: Possibly remove this after figuring out if internal stuff will break if it's not the default
  implicit object TruncatedIntSerializer extends Serializer[Int] with Comparator[Array[Byte]] {
    final def serialize(value: Int) = truncatedBytes(value)
    final def deserialize(bytes: Array[Byte]) = truncatedInt(bytes)
  
    final def compare(l: Array[Byte], r:Array[Byte]): Int = {
      val li = deserialize(l)
      val ri = deserialize(r)
  
      if(li < ri) -1
      else if (li > ri) 1
      else 0
    }
  }

  // TODO: Possibly remove this after figuring out if internal stuff will break if it's not the default
  implicit object TruncatedLongSerializer extends Serializer[Long] with Comparator[Array[Byte]] {
    final def serialize(value: Long) = truncatedBytes(value)
    final def deserialize(bytes: Array[Byte]) = truncatedLong(bytes)
  
    final def compare(l: Array[Byte], r: Array[Byte]): Int = {
      val li = deserialize(l)
      val ri = deserialize(r)
  
      if(li < ri) -1
      else if (li > ri) 1
      else 0
    }
  }
  
  /**
   * Convert an Int to a truncated byte array depending on how many
   * bytes are actually needed to store the value. Any leading bytes that are
   * zero are truncated.
   *
   * Examples:
   *  1 => 0x01
   *  255 => 0xff
   *  1024 => 0x0400
   */
  private def truncatedBytes(value: Int): Array[Byte] = {
    var len = 4
    var mask = 0xff000000

    // Figure out how many bytes we need to store the int
    while((value & mask) == 0 && len > 1) {
      len -= 1
      mask = mask >>> 8
    }

    val bytes = new Array[Byte](len)

    var v = value
    var i = len - 1

    while(i >= 0) {
      bytes(i) = (v & 0xff).byteValue
      v = v >>> 8
      i -= 1
    }

    bytes
  }

  private def truncatedInt(bytes: Array[Byte]): Int = {
    val len = bytes.length

    var idx = 0
    var value = 0

    while(idx < len) {
      value = (value << 8) | (0xff & bytes(idx))
      idx += 1
    }

    value
  }

  private def truncatedBytes(value: Long): Array[Byte] = {
    var len: Int = 8
    var mask: Long = 0xff00000000000000L

    // Figure out how many bytes we need to store the int
    while((value & mask) == 0 && len > 1) {
      len -= 1
      mask = mask >>> 8
    }

    val bytes: Array[Byte] = new Array[Byte](len)

    var v: Long = value
    var i: Int = len - 1

    while(i >= 0) {
      bytes(i) = (v & 0xff).byteValue
      v = v >>> 8
      i -= 1
    }

    bytes
  }

  private def truncatedLong(bytes: Array[Byte]): Long = {
    val len: Int = bytes.length

    var idx: Int = 0
    var value: Long = 0L

    while(idx < len) {
      value = (value << 8) | (0xff & bytes(idx))
      idx += 1
    }

    value
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy