selfcare init

This commit is contained in:
zhangsz
2025-03-03 11:40:37 +08:00
parent 19f09dd7ea
commit aca2bace68
692 changed files with 273972 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
Copyright (c) 2016 Charles Iliya Krempeaux <charles@reptile.ca> :: http://changelog.ca/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,64 @@
# go-oi
Package **oi** provides useful tools to be used with the Go programming language's standard "io" package.
For example, did you know that when you call the `Write` method on something that fits the `io.Writer`
interface, that it is possible that not everything was be written?!
I.e., that a _**short write**_ happened.
That just doing the following is (in general) **not** enough:
```
n, err := writer.Write(p)
```
That, for example, you should be checking if `err == io.ErrShortWrite`, and then maybe calling the `Write`
method again but only with what didn't get written.
For a simple example of this (that actually is **not** sufficient to solve this problem, but illustrates
the direction you would need to go to solve this problem is):
```
n, err := w.Write(p)
if io.ErrShortWrite == err {
n2, err2 := w.Write(p[n:])
}
```
Note that the second call to the `Write` method passed `p[n:]` (instead of just `p`), to account for the `n` bytes
already being written (with the first call to the `Write` method).
A more "production quality" version of this would likely be in a loop, but such that that the loop had "guards"
against looping forever, and also possibly looping for "too long".
Well package **oi** provides tools that helps you deal with this and other problems. For example, you
can handle a _**short write**_ with the following **oi** func:
```
n, err := oi.LongWrite(writer, p)
```
## Documention
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-oi
[![GoDoc](https://godoc.org/github.com/reiver/go-oi?status.svg)](https://godoc.org/github.com/reiver/go-oi)
## Example
```
import (
"github.com/reiver/go-oi"
)
// ...
p := []byte("It is important that this message be written!!!")
n, err := oi.LongWrite(writer, p)
if nil != err {
//@TODO: Handle error.
return
}
```

View File

@@ -0,0 +1,39 @@
/*
Package oi provides useful tools to be used with Go's standard "io" package.
For example, did you know that when you call the Write method on something that fits the io.Writer
interface, that it is possible that not everything was be written?!
I.e., that a 'short write' happened.
That just doing the following is (in general) not enough:
n, err := writer.Write(p)
That, for example, you should be checking if "err == io.ErrShortWrite", and then maybe calling the Write
method again but only with what didn't get written.
For a simple example of this (that actually is not sufficient to solve this problem, but illustrates
the direction you would need to go to solve this problem is):
n, err := w.Write(p)
if io.ErrShortWrite == err {
n2, err2 := w.Write(p[n:])
}
Note that the second call to the Write method passed "p[n:]" (instead of just "p"), to account for the "n" bytes
already being written (with the first call to the `Write` method).
A more "production quality" version of this would likely be in a loop, but such that that the loop had "guards"
against looping forever, and also possibly looping for "too long".
Well package oi provides tools that helps you deal with this and other problems. For example, you
can handle a 'short write' with the following oi func:
```
n, err := oi.LongWrite(writer, p)
```
*/
package oi

View File

@@ -0,0 +1,39 @@
package oi
import (
"io"
)
// LongWrite tries to write the bytes from 'p' to the writer 'w', such that it deals
// with "short writes" where w.Write would return an error of io.ErrShortWrite and
// n < len(p).
//
// Note that LongWrite still could return the error io.ErrShortWrite; but this
// would only be after trying to handle the io.ErrShortWrite a number of times, and
// then eventually giving up.
func LongWrite(w io.Writer, p []byte) (int64, error) {
numWritten := int64(0)
for {
//@TODO: Should check to make sure this doesn't get stuck in an infinite loop writting nothing!
n, err := w.Write(p)
numWritten += int64(n)
if nil != err && io.ErrShortWrite != err {
return numWritten, err
}
if !(n < len(p)) {
break
}
p = p[n:]
if len(p) < 1 {
break
}
}
return numWritten, nil
}

View File

@@ -0,0 +1,195 @@
package oi
import (
"github.com/reiver/go-oi/test"
"errors"
"testing"
)
func TestLongWrite(t *testing.T) {
tests := []struct{
String string
}{
{
String: "",
},
{
String: "apple",
},
{
String: "banana",
},
{
String: "cherry",
},
{
String: "Hello world!",
},
{
String: "😁😂😃😄😅😆😉😊😋😌😍😏😒😓😔😖😘😚😜😝😞😠😡😢😣😤😥😨😩😪😫😭😰😱😲😳😵😷",
},
{
String: "0123456789",
},
{
String: "٠١٢٣٤٥٦٧٨٩", // Arabic-Indic Digits
},
{
String: "۰۱۲۳۴۵۶۷۸۹", // Extended Arabic-Indic Digits
},
{
String: " Ⅱ Ⅲ Ⅳ Ⅵ Ⅶ Ⅷ Ⅸ Ⅺ Ⅻ ",
},
{
String: " ⅱ ⅲ ⅳ ⅵ ⅶ ⅷ ⅸ ⅺ ⅻ ⅿ",
},
{
String: "ↀ ↁ ↂ Ↄ ↄ ↅ ↆ ↇ ↈ",
},
}
for testNumber, test := range tests {
p := []byte(test.String)
var writer oitest.ShortWriter
n, err := LongWrite(&writer, p)
if nil != err {
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q; for %q.", testNumber, err, err.Error(), test.String)
continue
}
if expected, actual := int64(len([]byte(test.String))), n; expected != actual {
t.Errorf("For test #%d, expected %d, but actually got %d; for %q.", testNumber, expected, actual, test.String)
continue
}
if expected, actual := test.String, writer.String(); expected != actual {
t.Errorf("For test #%d, expected %q, but actually got %q", testNumber, expected, actual)
continue
}
}
}
func TestLongWriteExpectError(t *testing.T) {
tests := []struct{
String string
Expected string
Writes []int
Err error
}{
{
String: "apple",
Expected: "appl",
Writes: []int{2,2},
Err: errors.New("Crabapple!"),
},
{
String: "apple",
Expected: "appl",
Writes: []int{2,2,0},
Err: errors.New("Crabapple!!"),
},
{
String: "banana",
Expected: "banan",
Writes: []int{2,3},
Err: errors.New("bananananananana!"),
},
{
String: "banana",
Expected: "banan",
Writes: []int{2,3,0},
Err: errors.New("bananananananananananananana!!!"),
},
{
String: "cherry",
Expected: "cher",
Writes: []int{1,1,1,1},
Err: errors.New("C.H.E.R.R.Y."),
},
{
String: "cherry",
Expected: "cher",
Writes: []int{1,1,1,1,0},
Err: errors.New("C_H_E_R_R_Y"),
},
{
String: "Hello world!",
Expected: "Hello world",
Writes: []int{1,2,3,5},
Err: errors.New("Welcome!"),
},
{
String: "Hello world!",
Expected: "Hello world",
Writes: []int{1,2,3,5,0},
Err: errors.New("WeLcOmE!!!"),
},
{
String: " ",
Expected: " ",
Writes: []int{1,2,3,5,8,13},
Err: errors.New("Space, the final frontier"),
},
{
String: " ",
Expected: " ",
Writes: []int{1,2,3,5,8,13,0},
Err: errors.New("Space, the final frontier"),
},
}
for testNumber, test := range tests {
p := []byte(test.String)
writer := oitest.NewWritesThenErrorWriter(test.Err, test.Writes...)
n, err := LongWrite(writer, p)
if nil == err {
t.Errorf("For test #%d, expected to get an error, but actually did not get one: %v; for %q.", testNumber, err, test.String)
continue
}
if expected, actual := test.Err, err; expected != actual {
t.Errorf("For test #%d, expected to get error (%T) %q, but actually got (%T) %q; for %q.", testNumber, expected, expected.Error(), actual, actual.Error(), test.String)
continue
}
if expected, actual := int64(len(test.Expected)), n; expected != actual {
t.Errorf("For test #%d, expected number of bytes written to be %d = len(%q), but actually was %d = len(%q); for %q.", testNumber, expected, test.Expected, actual, writer.String(), test.String)
continue
}
}
}

View File

@@ -0,0 +1,28 @@
package oi
import (
"io"
)
// LongWriteByte trys to write the byte from 'b' to the writer 'w', such that it deals
// with "short writes" where w.Write would return an error of io.ErrShortWrite and
// n < 1.
//
// Note that LongWriteByte still could return the error io.ErrShortWrite; but this
// would only be after trying to handle the io.ErrShortWrite a number of times, and
// then eventually giving up.
func LongWriteByte(w io.Writer, b byte) error {
var buffer [1]byte
p := buffer[:]
buffer[0] = b
numWritten, err := LongWrite(w, p)
if 1 != numWritten {
return io.ErrShortWrite
}
return err
}

View File

@@ -0,0 +1,101 @@
package oi
import (
"github.com/reiver/go-oi/test"
"testing"
)
func TestLongWriteByte(t *testing.T) {
tests := []struct{
Byte byte
}{}
for b := byte(' '); b <= byte('/'); b++ {
test := struct {
Byte byte
}{
Byte:b,
}
tests = append(tests, test)
}
for b := byte('0'); b <= byte('9'); b++ {
test := struct {
Byte byte
}{
Byte:b,
}
tests = append(tests, test)
}
for b := byte(':'); b <= byte('@'); b++ {
test := struct {
Byte byte
}{
Byte:b,
}
tests = append(tests, test)
}
for b := byte('A'); b <= byte('Z'); b++ {
test := struct {
Byte byte
}{
Byte:b,
}
tests = append(tests, test)
}
for b := byte('['); b <= byte('`'); b++ {
test := struct {
Byte byte
}{
Byte:b,
}
tests = append(tests, test)
}
for b := byte('a'); b <= byte('z'); b++ {
test := struct {
Byte byte
}{
Byte:b,
}
tests = append(tests, test)
}
for b := byte('{'); b <= byte('~'); b++ {
test := struct {
Byte byte
}{
Byte:b,
}
tests = append(tests, test)
}
for testNumber, test := range tests {
var writer oitest.ShortWriter
err := LongWriteByte(&writer, test.Byte)
if nil != err {
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q; for %d (%q).", testNumber, err, err.Error(), test.Byte, string(test.Byte))
continue
}
if expected, actual := string(test.Byte), writer.String(); expected != actual {
t.Errorf("For test #%d, expected %q, but actually got %q", testNumber, expected, actual)
continue
}
}
}

View File

@@ -0,0 +1,39 @@
package oi
import (
"io"
)
// LongWriteString tries to write the bytes from 's' to the writer 'w', such that it deals
// with "short writes" where w.Write (or w.WriteString) would return an error of io.ErrShortWrite
// and n < len(s).
//
// Note that LongWriteString still could return the error io.ErrShortWrite; but this
// would only be after trying to handle the io.ErrShortWrite a number of times, and
// then eventually giving up.
func LongWriteString(w io.Writer, s string) (int64, error) {
numWritten := int64(0)
for {
//@TODO: Should check to make sure this doesn't get stuck in an infinite loop writting nothing!
n, err := io.WriteString(w, s)
numWritten += int64(n)
if nil != err && io.ErrShortWrite != err {
return numWritten, err
}
if !(n < len(s)) {
break
}
s = s[n:]
if len(s) < 1 {
break
}
}
return numWritten, nil
}

View File

@@ -0,0 +1,191 @@
package oi
import (
"github.com/reiver/go-oi/test"
"errors"
"testing"
)
func TestLongWriteString(t *testing.T) {
tests := []struct{
String string
}{
{
String: "",
},
{
String: "apple",
},
{
String: "banana",
},
{
String: "cherry",
},
{
String: "Hello world!",
},
{
String: "😁😂😃😄😅😆😉😊😋😌😍😏😒😓😔😖😘😚😜😝😞😠😡😢😣😤😥😨😩😪😫😭😰😱😲😳😵😷",
},
{
String: "0123456789",
},
{
String: "٠١٢٣٤٥٦٧٨٩", // Arabic-Indic Digits
},
{
String: "۰۱۲۳۴۵۶۷۸۹", // Extended Arabic-Indic Digits
},
{
String: " Ⅱ Ⅲ Ⅳ Ⅵ Ⅶ Ⅷ Ⅸ Ⅺ Ⅻ ",
},
{
String: " ⅱ ⅲ ⅳ ⅵ ⅶ ⅷ ⅸ ⅺ ⅻ ⅿ",
},
{
String: "ↀ ↁ ↂ Ↄ ↄ ↅ ↆ ↇ ↈ",
},
}
for testNumber, test := range tests {
var writer oitest.ShortWriter
n, err := LongWriteString(&writer, test.String)
if nil != err {
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q; for %q.", testNumber, err, err.Error(), test.String)
continue
}
if expected, actual := int64(len([]byte(test.String))), n; expected != actual {
t.Errorf("For test #%d, expected %d, but actually got %d; for %q.", testNumber, expected, actual, test.String)
continue
}
if expected, actual := test.String, writer.String(); expected != actual {
t.Errorf("For test #%d, expected %q, but actually got %q", testNumber, expected, actual)
continue
}
}
}
func TestLongWriteStringExpectError(t *testing.T) {
tests := []struct{
String string
Expected string
Writes []int
Err error
}{
{
String: "apple",
Expected: "appl",
Writes: []int{2,2},
Err: errors.New("Crabapple!"),
},
{
String: "apple",
Expected: "appl",
Writes: []int{2,2,0},
Err: errors.New("Crabapple!!"),
},
{
String: "banana",
Expected: "banan",
Writes: []int{2,3},
Err: errors.New("bananananananana!"),
},
{
String: "banana",
Expected: "banan",
Writes: []int{2,3,0},
Err: errors.New("bananananananananananananana!!!"),
},
{
String: "cherry",
Expected: "cher",
Writes: []int{1,1,1,1},
Err: errors.New("C.H.E.R.R.Y."),
},
{
String: "cherry",
Expected: "cher",
Writes: []int{1,1,1,1,0},
Err: errors.New("C_H_E_R_R_Y"),
},
{
String: "Hello world!",
Expected: "Hello world",
Writes: []int{1,2,3,5},
Err: errors.New("Welcome!"),
},
{
String: "Hello world!",
Expected: "Hello world",
Writes: []int{1,2,3,5,0},
Err: errors.New("WeLcOmE!!!"),
},
{
String: " ",
Expected: " ",
Writes: []int{1,2,3,5,8,13},
Err: errors.New("Space, the final frontier"),
},
{
String: " ",
Expected: " ",
Writes: []int{1,2,3,5,8,13,0},
Err: errors.New("Space, the final frontier"),
},
}
for testNumber, test := range tests {
writer := oitest.NewWritesThenErrorWriter(test.Err, test.Writes...)
n, err := LongWriteString(writer, test.String)
if nil == err {
t.Errorf("For test #%d, expected to get an error, but actually did not get one: %v; for %q.", testNumber, err, test.String)
continue
}
if expected, actual := test.Err, err; expected != actual {
t.Errorf("For test #%d, expected to get error (%T) %q, but actually got (%T) %q; for %q.", testNumber, expected, expected.Error(), actual, actual.Error(), test.String)
continue
}
if expected, actual := int64(len(test.Expected)), n; expected != actual {
t.Errorf("For test #%d, expected number of bytes written to be %d = len(%q), but actually was %d = len(%q); for %q.", testNumber, expected, test.Expected, actual, writer.String(), test.String)
continue
}
}
}

View File

@@ -0,0 +1,5 @@
/*
Package oitest provides useful tools to be used for testing implementations of interfaces in Go's standard "io" package.
*/
package oitest

View File

@@ -0,0 +1,12 @@
package oitest
import (
"math/rand"
"time"
)
var (
randomness = rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
)

View File

@@ -0,0 +1,80 @@
package oitest
import (
"bytes"
"io"
)
// ShortWriter is useful for testing things that makes use of
// or accept an io.Writer.
//
// In particular, it is useful to see if that thing can handle
// an io.Writer that does a "short write".
//
// A "short write" is the case when a call like:
//
// n, err := w.Write(p)
//
// Returns an n < len(p) and err == io.ErrShortWrite.
//
// A thing that can "handle" this situation might try
// writing again, but only what didn't get written.
//
// For a simple example of this:
//
// n, err := w.Write(p)
//
// if io.ErrShortWrite == err {
// n2, err2 := w.Write(p[n:])
// }
//
// Note that the second call to the Write method passed
// 'p[n:]' (instead of just 'p'), to account for 'n' bytes
// already written (with the first call to the Write
// method).
//
// A more "production quality" version of this would likely
// be in a loop, but such that that loop had "guards" against
// looping forever, and also possibly looping for "too long".
type ShortWriter struct {
buffer bytes.Buffer
}
// Write makes it so ShortWriter fits the io.Writer interface.
//
// ShortWriter's version of Write will "short write" if len(p) >= 2,
// else it will
func (w *ShortWriter) Write(p []byte) (int, error) {
if len(p) < 1 {
return 0, nil
}
m := 1
if limit := len(p)-1; limit > 1 {
m += randomness.Intn(len(p)-1)
}
n, err := w.buffer.Write(p[:m])
err = nil
if n < len(p) {
err = io.ErrShortWrite
}
return n, err
}
// Returns what was written to the ShortWriter as a []byte.
func (w ShortWriter) Bytes() []byte {
return w.buffer.Bytes()
}
// Returns what was written to the ShortWriter as a string.
func (w ShortWriter) String() string {
return w.buffer.String()
}

View File

@@ -0,0 +1,96 @@
package oitest
import (
"bytes"
"io"
)
// WritesThenErrorWriter is useful for testing things that makes
// use of or accept an io.Writer.
//
// In particular, it is useful to see if that thing can handle
// an io.Writer that does a "short write".
//
// A "short write" is the case when a call like:
//
// n, err := w.Write(p)
//
// Returns an n < len(p) and err == io.ErrShortWrite.
//
// A thing that can "handle" this situation might try
// writing again, but only what didn't get written.
//
// For a simple example of this:
//
// n, err := w.Write(p)
//
// if io.ErrShortWrite == err {
// n2, err2 := w.Write(p[n:])
// }
//
// Note that the second call to the Write method passed
// 'p[n:]' (instead of just 'p'), to account for 'n' bytes
// already written (with the first call to the Write
// method).
//
// A more "production quality" version of this would likely
// be in a loop, but such that that loop had "guards" against
// looping forever, and also possibly looping for "too long".
type WritesThenErrorWriter struct {
buffer bytes.Buffer
err error
numbersWritten []int
writeNumber int
}
// NewWritesThenErrorWriter returns a new *WritesThenErrorWriter.
func NewWritesThenErrorWriter(err error, numbersWritten ...int) *WritesThenErrorWriter {
slice := make([]int, len(numbersWritten))
copy(slice, numbersWritten)
writer := WritesThenErrorWriter{
err:err,
numbersWritten:slice,
writeNumber:0,
}
return &writer
}
// Write makes it so *WritesThenErrorWriter fits the io.Writer interface.
//
// *WritesThenErrorWriter's version of Write will "short write" all but
// the last call to write, where it will return the specified error (which
// could, of course, be nil, if that was specified).
func (writer *WritesThenErrorWriter) Write(p []byte) (int, error) {
m := writer.numbersWritten[writer.writeNumber]
writer.buffer.Write(p[:m])
writer.writeNumber++
if len(writer.numbersWritten) == writer.writeNumber {
return m, writer.err
}
return m, io.ErrShortWrite
}
// Returns what was written to the ShortWriter as a []byte.
func (w WritesThenErrorWriter) Bytes() []byte {
return w.buffer.Bytes()
}
// Returns what was written to the ShortWriter as a string.
func (w WritesThenErrorWriter) String() string {
return w.buffer.String()
}

View File

@@ -0,0 +1,38 @@
package oi
import (
"io"
)
// WriteNopCloser takes an io.Writer and returns an io.WriteCloser where
// calling the Write method on the returned io.WriterCloser calls the
// Write method on the io.Writer it received, but whre calling the Close
// method on the returned io.WriterCloser does "nothing" (i.e., is a "nop").
//
// This is useful in cases where an io.WriteCloser is expected, but you
// only have an io.Writer (where closing doesn't make sense) and you
// need to make your io.Writer fit. (I.e., you need an adaptor.)
func WriteNopCloser(w io.Writer) io.WriteCloser {
wc := internalWriteNopCloser{
writer:w,
}
return &wc
}
type internalWriteNopCloser struct {
writer io.Writer
}
func (wc * internalWriteNopCloser) Write(p []byte) (n int, err error) {
return wc.writer.Write(p)
}
func (wc * internalWriteNopCloser) Close() error {
return nil
}