Skip to content

Commit 19a9163

Browse files
Mark DelanyMark Delany
authored andcommitted
Initial check-in
1 parent 0bf24e1 commit 19a9163

29 files changed

Lines changed: 2910 additions & 1 deletion

.github/workflows/codecov.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: codecov
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
run:
7+
strategy:
8+
matrix:
9+
os: [ ubuntu-latest, macos-latest, windows-latest ]
10+
go: [ 1.20.x ]
11+
runs-on: ${{ matrix.os }}
12+
steps:
13+
- uses: actions/checkout@main
14+
15+
- name: Set up Go
16+
uses: actions/setup-go@v3
17+
with:
18+
go-version: ${{ matrix.go }}
19+
20+
- name: Generate Coverage Report
21+
run: go test ./... -coverprofile=coverage.txt -covermode=atomic
22+
23+
- name: Upload Coverage Report to Codecov
24+
uses: codecov/codecov-action@v3
25+
with:
26+
file: ./coverage.txt
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# For most projects, this workflow file will not need changing; you simply need
2+
# to commit it to your repository.
3+
#
4+
# You may wish to alter this file to override the set of languages analyzed,
5+
# or to provide custom queries or build logic.
6+
#
7+
# ******** NOTE ********
8+
# We have attempted to detect the languages in your repository. Please check
9+
# the `language` matrix defined below to confirm you have the correct set of
10+
# supported CodeQL languages.
11+
#
12+
name: "CodeQL"
13+
14+
on:
15+
push:
16+
branches: [ main ]
17+
pull_request:
18+
# The branches below must be a subset of the branches above
19+
branches: [ main ]
20+
schedule:
21+
- cron: '25 0 * * 5'
22+
23+
jobs:
24+
analyze:
25+
name: Analyze
26+
runs-on: ubuntu-latest
27+
permissions:
28+
actions: read
29+
contents: read
30+
security-events: write
31+
32+
strategy:
33+
fail-fast: false
34+
matrix:
35+
language: [ 'go' ]
36+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37+
# Learn more about CodeQL language support at https://git.io/codeql-language-support
38+
39+
steps:
40+
- name: Checkout repository
41+
uses: actions/checkout@v2
42+
43+
# Initializes the CodeQL tools for scanning.
44+
- name: Initialize CodeQL
45+
uses: github/codeql-action/init@v2
46+
with:
47+
languages: ${{ matrix.language }}
48+
# If you wish to specify custom queries, you can do so here or in a config file.
49+
# By default, queries listed here will override any specified in a config file.
50+
# Prefix the list here with "+" to use these queries and those in the config file.
51+
# queries: ./path/to/local/query, your-org/your-repo/queries@main
52+
53+
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54+
# If this step fails, then you should remove it and run the build manually (see below)
55+
- name: Autobuild
56+
uses: github/codeql-action/autobuild@v2
57+
58+
# ℹ️ Command-line programs to run using the OS shell.
59+
# 📚 https://git.io/JvXDl
60+
61+
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62+
# and modify them (or add more) to build your code if your project
63+
# uses a compiled language
64+
65+
#- run: |
66+
# make bootstrap
67+
# make release
68+
69+
- name: Perform CodeQL Analysis
70+
uses: github/codeql-action/analyze@v2

.github/workflows/go.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Build
2+
on:
3+
- push
4+
- pull_request
5+
jobs:
6+
build:
7+
name: Build and Test
8+
strategy:
9+
matrix:
10+
go: [ 1.20.x ]
11+
runs-on:
12+
- ubuntu-latest
13+
steps:
14+
- name: Set up Go
15+
uses: actions/setup-go@v3
16+
with:
17+
go-version: ${{ matrix.go }}
18+
19+
- name: Check out code
20+
uses: actions/checkout@v3
21+
22+
- name: Build
23+
run: go build -v ./...
24+
25+
- name: Test
26+
run: go test -v ./...

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
# vendor/
19+
20+
# Go workspace file
21+
go.work

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Mark Delany <puz@bravo.emu.st>

ChangeLog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# netstring Change Log
2+
### v1.0.0 -- 2023-05-10
3+
* Initial public release.
4+
### v2.0.0 -- 2023-05-16
5+
* Refactor to more closely match encoder/json
6+
* Remove notifier and concurrency controls
7+
* NewDecoder now accepts an io.Reader
8+
* Decoder now returns io.EOF at end of stream
9+
* Lay groundwork for introduction of Marshal and Unmarshal
10+
### v2.1.0 -- 2023-05-16
11+
* Add Encoder.Marshal() and Decoder.Marshal()

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 2-Clause License
22

3-
Copyright (c) 2023, Mark
3+
Copyright (c) 2023, Mark Delany
44

55
Redistribution and use in source and binary forms, with or without
66
modification, are permitted provided that the following conditions are met:

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
all:
2+
@echo Make targets are 'fmt' and 'tests'
3+
4+
.PHONY: fmt
5+
fmt:
6+
find . -name '*.go' -type f -print | xargs gofmt -s -w
7+
8+
.PHONY: test tests
9+
test tests:
10+
go test ./...
11+
go vet ./...

README.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<!-- Always newline after period so diffs are easier to read. -->
2+
# netstring - Robust encoding and decoding of netstrings across network connections
3+
4+
## Introduction
5+
6+
The `netstring` package is a go implementation of
7+
[netstring](https://cr.yp.to/proto/netstrings.txt) serialization as originally specified
8+
by D. J. Bernstein [djb](https://cr.yp.to/cv.html).
9+
10+
`netstring` provides an Encoder and a Decoder.
11+
12+
A netstring.Encoder writes go types as encoded netstrings to a supplied io.Writer. Encoder
13+
has helper functions to encode basic go types such as bool, ints, floats, strings and
14+
bytes to netstrings. Structs, maps and other complex data structures are not supported. A
15+
netstring.Decoder accepts a byte-stream via its io.Reader and presents successfully parsed
16+
netstrings via the Decode.Decode() and Decoder.DecodeKeyed() functions.
17+
18+
Alternatively applications can use the message level Encoder.Marshal() and
19+
Decoder.Unmarshal() functions to encode and decode a simple struct containing "keyed"
20+
netstrings with an end-of-message sentinel.
21+
22+
The overall goal of this package is to make it easy to attach netstring Encoders and
23+
Decoders to network connections or other reliable transports so that applications can
24+
exchange messages with either the lower level Encode*()/Decode*() functions or the higher
25+
level Marshal() and Unmarshal() functions.
26+
27+
### Project Status
28+
29+
[![Build Status](https://github.com/markdingo/netstring/actions/workflows/go.yml/badge.svg)](https://github.com/markdingo/netstring/actions/workflows/go.yml)
30+
[![codecov](https://codecov.io/gh/markdingo/netstring/branch/main/graph/badge.svg)](https://codecov.io/gh/markdingo/netstring)
31+
[![CodeQL](https://github.com/markdingo/netstring/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/markdingo/netstring/actions/workflows/codeql-analysis.yml)
32+
[![Go Report Card](https://goreportcard.com/badge/github.com/markdingo/netstring)](https://goreportcard.com/report/github.com/markdingo/netstring)
33+
[![Go Reference](https://pkg.go.dev/badge/github.com/markdingo/netstring.svg)](https://pkg.go.dev/github.com/markdingo/netstring)
34+
35+
## Background
36+
37+
A netstring is a simple serialization technique where each value is expressed in the form
38+
[length] ":" [value] "," where [value] is the payload, [length] is the length of [value]
39+
in decimal bytes and the colon and comma are leading and trailing delimiters respectively.
40+
An example of the value "The Hitchhiker's Guide to the Galaxy - D.A." encoded as a
41+
netstring is:
42+
43+
"42:The Hitchhiker's Guide to the Galaxy - DA.,"
44+
45+
Storing binary values in netstrings, while possible, is not recommended for obvious
46+
reasons of incompatible CPU architectures.
47+
Best practise is to convert all binary values to strings prior to encoding.
48+
To assist in this best practice, helper functions are available to encode basic go-types
49+
such as ints and floats to netstrings.
50+
E.g. the function Encoder.EncodeInt() converts int(2^16) to the netstring:
51+
52+
"5:65536,"
53+
54+
## "Keyed" netstrings
55+
56+
In addition to standard netstrings, this package supports "keyed" netstrings which are
57+
nothing more than netstrings where the first byte signifies a "type" which describes the
58+
value in some useful way to the application.
59+
60+
The benefit of "keyed" netstrings is that they create a simple self-describing typing system
61+
which allows applications to encode multiple netstrings in a message without having to
62+
rely on positional order or nested netstrings to convey semantics or encapsulate a message.
63+
64+
To demonstrate the benefits of "keyed" netstrings, say you want to encode Name, Age and
65+
Country as netstrings to be transmitted to a remote service? With standard netstrings
66+
you'd have to agree on the positional order for each value, say netstring #0 for Name, #1
67+
for Age and so on.
68+
Here is what that series of netstrings looks like:
69+
70+
"4:Name,3:Age,7:Country,"
71+
72+
If any of these values are optional, you'd still have to provide an empty netstring to
73+
ensure positional order is maintained which creates the dilemma as to whether an empty
74+
netstring is a value or a place-holder.
75+
76+
With "keyed" netstrings the values can be presented in any order and optional values are
77+
simply omitted.
78+
In the above example if we assign the "key" of 'f' to First Name, 'l' to Last Name, 'a' to
79+
Age and 'h' to Height the series of "keyed" netstrings looks like:
80+
81+
"11:fFirst Name,10:lLast Name,4:aAge,7:hHeight,"
82+
83+
But if Age is optional the series of netstrings simply becomes:
84+
85+
"7:hHeight,11:fFirst Name,10:lLast Name,"
86+
87+
Note the change of order as well as the missing 'a' netstring?
88+
89+
## Installation
90+
91+
`netstring` is available using the standard `go get` command or it should automatically be
92+
installed by `go tidy` or `go build` if your project uses go modules.
93+
94+
Install with:
95+
96+
```
97+
go get github.com/markdingo/netstring
98+
```
99+
100+
and run tests with:
101+
102+
```
103+
go test github.com/markdingo/netstring
104+
```
105+
106+
and of course once installed, package documentation is available via:
107+
108+
```
109+
go doc github.com/markdingo/netstring
110+
```
111+
112+
## Usage
113+
114+
``` go
115+
import "github.com/markdingo/netstring"
116+
```
117+
118+
To create a message of netstrings, call NewEncoder() then call the various Encode*()
119+
functions to encode the basic go types. This code fragment encodes a message into a
120+
bytes.Buffer.
121+
122+
```
123+
var buf bytes.Buffer
124+
enc := netstring.NewEncoder(&buf)
125+
enc.EncodeInt('a', 21) // Age
126+
enc.EncodeString('C', "Iceland") // Country
127+
enc.EncodeString('n', "Bjorn") // Name
128+
enc.EncodeBytes('z') // End-of-message sentinel
129+
fmt.Println(buf.String()) // "3:a21,8:CIceland,6:nBjorn,1:z,"
130+
```
131+
132+
And this fragment decodes the same message.
133+
134+
```
135+
dec := netstring.NewDecoder(&buf)
136+
k, v, e := dec.DecodeKeyed() // k=a, v=21
137+
k, v, e = dec.DecodeKeyed() // k=C, v=Iceland
138+
k, v, e = dec.DecodeKeyed() // k=n, v=Bjorn
139+
k, v, e = dec.DecodeKeyed() // k=z End-Of-Message
140+
```
141+
142+
The message can more easily be encoded with Marshal() as this fragment shows:
143+
144+
```
145+
type record struct {
146+
Age int `netstring:"a"`
147+
Country string `netstring:"c"`
148+
Name string `netstring:"n"`
149+
}
150+
151+
var buf bytes.Buffer
152+
enc := netstring.NewEncoder(&buf)
153+
out := &record{21, "Iceland", "Bjorn"}
154+
enc.Marshal('z', out)
155+
```
156+
157+
and more easily decoded with Unmarshal() as this fragment shows:
158+
159+
```
160+
dec := netstring.NewDecoder(&buf)
161+
in := &record{}
162+
dec.Unmarshal('z', in)
163+
```
164+
165+
Full working programs can be found in the _examples directory.
166+
167+
### Community
168+
169+
If you have any problems using `netstring` or suggestions on how it can do a better job,
170+
don't hesitate to create an [issue](https://github.com/markdingo/netstring/issues) on the
171+
project home page. This package can only improve with your feedback.
172+
173+
### Copyright and License
174+
175+
`netstring` is Copyright :copyright: 2023 Mark Delany and is licensed under the BSD
176+
2-Clause "Simplified" License.

TODO.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Rearrange Decoder to match json.Decoder. That is, give it an io.Reader
2+
from which it consumes bytes.
3+
4+
Todo
5+
----
6+
7+
Done
8+
----
9+
- Assume read blocks until io.EOF
10+
- Can return io.EOF
11+
- Should be able to get rid of the queue
12+
- Get calls parse which read io.Reader as needed
13+
- parse returns if a complete netstring is available. It does not
14+
loop around and create multiple netstrings.
15+
- Get rid of the notifier
16+
- Get rid of locking
17+
- Debug unmarshal
18+
- Replace Put with Encode?
19+
Fix README.md
20+
Marshal/Unmarshal examples in doc.go and README.md
21+
22+
Replace Decoder.Get() with Decoder.Decode() and GetKeyed with DecodeKeyed()
23+
Then Marshal can use the same loop with GetKeyed() until eom or EOF.
24+
25+
Replace Encoder.Put* with Encoder.Encode*

0 commit comments

Comments
 (0)