Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/zero-deps/proto

Lightweight and fast serialization library for Scala 2/3 based on Protocol Buffers with macros
https://github.com/zero-deps/proto

macros protobuf scala

Last synced: 2 months ago
JSON representation

Lightweight and fast serialization library for Scala 2/3 based on Protocol Buffers with macros

Awesome Lists containing this project

README

        

# proto

[![proto Scala version support](https://index.scala-lang.org/zero-deps/proto/proto/latest-by-scala-version.svg?platform=jvm)](https://index.scala-lang.org/zero-deps/proto/proto)
[![proto Scala version support](https://index.scala-lang.org/zero-deps/proto/proto/latest-by-scala-version.svg?platform=sjs1)](https://index.scala-lang.org/zero-deps/proto/proto)
[![proto Scala version support](https://index.scala-lang.org/zero-deps/proto/proto/latest-by-scala-version.svg?platform=native0.5)](https://index.scala-lang.org/zero-deps/proto/proto)
![Production Ready](https://img.shields.io/badge/Project%20Stage-Production%20Ready-brightgreen.svg)

Lightweight and fast serialization library for Scala 2/3 based on Protocol Buffers with macros magic.

## Motivation

Serialization library for Scala that can be used either for long term store models and short term models.
With easy to migrate possibility.

- Lightweight
- Fast
- Protocol Buffers compatible
- No .proto files
- No model convertation
- Type safe
- Serialization/deserialization without changes in models
- Possibility to use specific types in model

## Install

Add dependency:
```
libraryDependencies += "io.github.zero-deps" %% "proto" % "latest.integration"
```

Dependency as a git-submodule is also supported.

## Benchmark #1

data | | library | scala-2 | scala-3
---- | ------ |:-------------- | -------------: | ------------:
data | decode | java | ` 44079.139` | ` 38979.697`
data | decode | jackson | ` 176941.468` | ` 188555.562`
data | decode | jsoniter-scala | ` 483788.001` | no support for Scala 3
data | decode | boopickle | ` 2885610.648` | no support for Scala 3
data | decode | proto | ` 3383845.458` | `3776688.591`
data | decode | scalapb | ` 3270691.564` | `3893847.420`

data | | library | scala-2 | scala-3
---- | ------ |:-------------- | -------------: | ------------:
data | encode | java | ` 220444.268` | ` 217484.396`
data | encode | jackson | ` 431318.803` | ` 384863.249`
data | encode | jsoniter-scala | ` 1054650.233` | no support for Scala 3
data | encode | boopickle | ` 1520834.519` | no support for Scala 3
data | encode | proto | ` 3186951.441` | `2965427.382`
data | encode | scalapb | ` 3628779.864` | `3972905.402`

data | | library | scala-2 | scala-3
---- | ------ |:-------------- | -------------: | ------------:
msg | decode | jsoniter-scala | ` 3486552.303` | no support for Scala 3
msg | decode | proto | ` 5825174.170` | `6395557.251`
msg | decode | scalapb | ` 4898257.671` | `6902064.854`

data | | library | scala-2 | scala-3
---- | ------ |:-------------- | -------------: | ------------:
msg | encode | jsoniter-scala | ` 6372602.760` | no support for Scala 3
msg | encode | proto | ` 6487748.959` | `6745673.393`
msg | encode | scalapb | ` 9202135.451` | `9056962.541`

### environment

Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz\
16 GB 2133 MHz LPDDR3\
Java 15

### run benchmark

```bash
sbt
project bench
++ 3.0.0
jmh:run -i 2 -wi 1 -f1 -t1
```

## Benchmark #2

data | | library | scala-3
---- | ------ |:-------------- | -------------:
data | decode | java | ` 92130,460`
data | decode | jackson | ` 517036,354`
data | decode | proto | ` 6716619,956`

data | | library | scala-3
---- | ------ |:-------------- | -------------:
data | encode | java | ` 537462,511`
data | encode | jackson | ` 882065,311`
data | encode | proto | ` 9380874,587`

data | | library | scala-3
---- | ------ |:-------------- | -------------:
msg | decode | proto | `11733555,275`

data | | library | scala-3
---- | ------ |:-------------- | -------------:
msg | encode | proto | `18486833,582`

### environment

Apple M1\
16 GB\
Java 21

### run benchmark

```bash
sbt
project bench
++ 3.0.0
jmh:run -i 2 -wi 1 -f1 -t1
```

## Usage

You can pick one of the way how to define field number:
- with annotation `@proto.N` and use `caseCodecAuto`
- explicitly specify nums `caseCodecNums(Symbol("field1")->1, Symbol("field2")->2)`
- field numbers by index `caseCodecIdx`

You can use annotation `@proto.RestrictedN` to restrict usage of specified field numbers. Can be used with classes or traits.

```scala
import scala.collection.immutable.TreeMap
import proto.{encode, decode, N}
import proto.{caseCodecIdx, caseCodecNums, caseCodecAuto}

final case class VectorClock(versions: TreeMap[String, Long])
@RestrictedN(3,4)
final case class Equipment(@N(1) id: String, @N(2) tpe: String)
final case class Car(id: String, color: Int, equipment: List[Equipment], vc: VectorClock)

implicit val tuple2Codec = caseCodecIdx[Tuple2[String, Long]] //codec for TreeMap[String, Long]

implicit val vectorClockCodec = caseCodecIdx[VectorClock]
implicit val equipmentCodec = caseCodecAuto[Equipment]
implicit val carCodec = caseCodecNums[Car]('id->1, 'color->4, 'equipment->2, 'vc->3)

val vc = VectorClock(versions=TreeMap.empty)
val equipment = List(Equipment(id="1", tpe="123"), Equipment(id="2", tpe="456"))
val car = Car(id="1", color=16416882, equipment=equipment, vc=vc)
//encode
val bytes: Array[Byte] = encode(car)
//decode
val car2: Car = decode[Car](bytes)
```

More examples in [testing.scala](src/test/scala/testing.scala)