docs: add more documentation to each of the modules in vlib (#13043)
parent
287331bc19
commit
6e6d51a1c9
|
@ -1 +1,8 @@
|
||||||
# Documentation for all included modules...
|
# `vlib` Documentation
|
||||||
|
|
||||||
|
`vlib` is the term for all modules included by default with V and
|
||||||
|
maintained as part of the V source code repository.
|
||||||
|
|
||||||
|
Some included modules depend on third party libraries, and these are kept
|
||||||
|
separate in the `thirdparty` directory at the root level of the source
|
||||||
|
repository.
|
||||||
|
|
|
@ -1,4 +1,16 @@
|
||||||
## Description:
|
## Description:
|
||||||
|
|
||||||
`arrays` provides some generic functions for processing arrays.
|
`arrays` is a module that provides utility functions to make working with arrays easier.
|
||||||
|
|
||||||
|
## Examples:
|
||||||
|
|
||||||
|
```v
|
||||||
|
import arrays
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := [1, 5, 7, 0, 9]
|
||||||
|
assert arrays.min(a) ? == 0
|
||||||
|
assert arrays.max(a) ? == 9
|
||||||
|
assert arrays.idx_min(a) ? == 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -6,9 +6,10 @@ module arrays
|
||||||
// - merge - combine two sorted arrays and maintain sorted order
|
// - merge - combine two sorted arrays and maintain sorted order
|
||||||
// - chunk - chunk array to arrays with n elements
|
// - chunk - chunk array to arrays with n elements
|
||||||
// - window - get snapshots of the window of the given size sliding along array with the given step, where each snapshot is an array
|
// - window - get snapshots of the window of the given size sliding along array with the given step, where each snapshot is an array
|
||||||
// - zip - concat two arrays into one map
|
// - TODO: zip - merge two arrays by interleaving e.g. arrays.zip([1,3,5], [2,4,6]) => [1,2,3,4,5,6]
|
||||||
|
|
||||||
// min returns the minimum value in the array
|
// min returns the minimum value in the array
|
||||||
|
// Example: arrays.min([1,2,3,0,9]) // => 0
|
||||||
pub fn min<T>(a []T) ?T {
|
pub fn min<T>(a []T) ?T {
|
||||||
if a.len == 0 {
|
if a.len == 0 {
|
||||||
return error('.min called on an empty array')
|
return error('.min called on an empty array')
|
||||||
|
@ -23,6 +24,7 @@ pub fn min<T>(a []T) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// max returns the maximum the maximum value in the array
|
// max returns the maximum the maximum value in the array
|
||||||
|
// Example: arrays.max([1,2,3,0,9]) // => 9
|
||||||
pub fn max<T>(a []T) ?T {
|
pub fn max<T>(a []T) ?T {
|
||||||
if a.len == 0 {
|
if a.len == 0 {
|
||||||
return error('.max called on an empty array')
|
return error('.max called on an empty array')
|
||||||
|
@ -37,6 +39,7 @@ pub fn max<T>(a []T) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// idx_min returns the index of the minimum value in the array
|
// idx_min returns the index of the minimum value in the array
|
||||||
|
// Example: arrays.idx_min([1,2,3,0,9]) // => 3
|
||||||
pub fn idx_min<T>(a []T) ?int {
|
pub fn idx_min<T>(a []T) ?int {
|
||||||
if a.len == 0 {
|
if a.len == 0 {
|
||||||
return error('.idx_min called on an empty array')
|
return error('.idx_min called on an empty array')
|
||||||
|
@ -53,6 +56,7 @@ pub fn idx_min<T>(a []T) ?int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// idx_max returns the index of the maximum value in the array
|
// idx_max returns the index of the maximum value in the array
|
||||||
|
// Example: arrays.idx_max([1,2,3,0,9]) // => 4
|
||||||
pub fn idx_max<T>(a []T) ?int {
|
pub fn idx_max<T>(a []T) ?int {
|
||||||
if a.len == 0 {
|
if a.len == 0 {
|
||||||
return error('.idx_max called on an empty array')
|
return error('.idx_max called on an empty array')
|
||||||
|
@ -69,6 +73,7 @@ pub fn idx_max<T>(a []T) ?int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge two sorted arrays (ascending) and maintain sorted order
|
// merge two sorted arrays (ascending) and maintain sorted order
|
||||||
|
// Example: arrays.merge([1,3,5,7], [2,4,6,8]) // => [1,2,3,4,5,6,7,8]
|
||||||
[direct_array_access]
|
[direct_array_access]
|
||||||
pub fn merge<T>(a []T, b []T) []T {
|
pub fn merge<T>(a []T, b []T) []T {
|
||||||
mut m := []T{len: a.len + b.len}
|
mut m := []T{len: a.len + b.len}
|
||||||
|
@ -102,6 +107,8 @@ pub fn merge<T>(a []T, b []T) []T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// group n arrays into a single array of arrays with n elements
|
// group n arrays into a single array of arrays with n elements
|
||||||
|
// (an error will be generated if the type annotation is omitted)
|
||||||
|
// Example: arrays.group<int>([1,2,3],[4,5,6]) // => [[1, 4], [2, 5], [3, 6]]
|
||||||
pub fn group<T>(lists ...[]T) [][]T {
|
pub fn group<T>(lists ...[]T) [][]T {
|
||||||
mut length := if lists.len > 0 { lists[0].len } else { 0 }
|
mut length := if lists.len > 0 { lists[0].len } else { 0 }
|
||||||
// calculate length of output by finding shortest input array
|
// calculate length of output by finding shortest input array
|
||||||
|
@ -128,8 +135,8 @@ pub fn group<T>(lists ...[]T) [][]T {
|
||||||
return [][]T{}
|
return [][]T{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// chunk array to arrays with n elements
|
// chunk array into a single array of arrays where each element is the next `size` elements of the original
|
||||||
// example: arrays.chunk([1, 2, 3], 2) => [[1, 2], [3]]
|
// Example: arrays.chunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 2)) // => [[1, 2], [3, 4], [5, 6], [7, 8], [9]]
|
||||||
pub fn chunk<T>(list []T, size int) [][]T {
|
pub fn chunk<T>(list []T, size int) [][]T {
|
||||||
// allocate chunk array
|
// allocate chunk array
|
||||||
mut chunks := [][]T{cap: list.len / size + if list.len % size == 0 { 0 } else { 1 }}
|
mut chunks := [][]T{cap: list.len / size + if list.len % size == 0 { 0 } else { 1 }}
|
||||||
|
@ -163,8 +170,8 @@ pub struct WindowAttribute {
|
||||||
// - `size` - snapshot size
|
// - `size` - snapshot size
|
||||||
// - `step` - gap size between each snapshot, default is 1.
|
// - `step` - gap size between each snapshot, default is 1.
|
||||||
//
|
//
|
||||||
// example A: `arrays.window([1, 2, 3, 4], size: 2)` => `[[1, 2], [2, 3], [3, 4]]`
|
// Example: arrays.window([1, 2, 3, 4], size: 2) => [[1, 2], [2, 3], [3, 4]]
|
||||||
// example B: `arrays.window([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size: 3, step: 2)` => `[[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]`
|
// Example: arrays.window([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size: 3, step: 2) // => [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]
|
||||||
pub fn window<T>(list []T, attr WindowAttribute) [][]T {
|
pub fn window<T>(list []T, attr WindowAttribute) [][]T {
|
||||||
// allocate snapshot array
|
// allocate snapshot array
|
||||||
mut windows := [][]T{cap: list.len - attr.size + 1}
|
mut windows := [][]T{cap: list.len - attr.size + 1}
|
||||||
|
@ -183,10 +190,11 @@ pub fn window<T>(list []T, attr WindowAttribute) [][]T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sum up array, return nothing when array has no elements
|
// sum up array, return nothing when array has no elements
|
||||||
// NOTICE: currently V has bug that cannot make sum function takes custom struct with + operator overloaded.
|
//
|
||||||
|
// NOTICE: currently V has bug that cannot make sum function takes custom struct with + operator overloaded
|
||||||
// which means you can only pass array of numbers for now.
|
// which means you can only pass array of numbers for now.
|
||||||
// Future work: Fix generic operator overloading detection issue.
|
// TODO: Fix generic operator overloading detection issue.
|
||||||
// usage: `arrays.sum<int>([1, 2, 3, 4, 5])?` => `15`
|
// Example: arrays.sum<int>([1, 2, 3, 4, 5])? // => 15
|
||||||
pub fn sum<T>(list []T) ?T {
|
pub fn sum<T>(list []T) ?T {
|
||||||
if list.len == 0 {
|
if list.len == 0 {
|
||||||
return error('Cannot sum up array of nothing.')
|
return error('Cannot sum up array of nothing.')
|
||||||
|
@ -206,8 +214,8 @@ pub fn sum<T>(list []T) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// accumulates values with the first element and applying providing operation to current accumulator value and each elements.
|
// accumulates values with the first element and applying providing operation to current accumulator value and each elements.
|
||||||
// if the array is empty, then returns error.
|
// If the array is empty, then returns error.
|
||||||
// usage: `arrays.reduce([1, 2, 3, 4, 5], fn (t1 int, t2 int) int { return t1 * t2 })?` => `120`
|
// Example: arrays.reduce([1, 2, 3, 4, 5], fn (t1 int, t2 int) int { return t1 * t2 })? // => 120
|
||||||
pub fn reduce<T>(list []T, reduce_op fn (t1 T, t2 T) T) ?T {
|
pub fn reduce<T>(list []T, reduce_op fn (t1 T, t2 T) T) ?T {
|
||||||
if list.len == 0 {
|
if list.len == 0 {
|
||||||
return error('Cannot reduce array of nothing.')
|
return error('Cannot reduce array of nothing.')
|
||||||
|
@ -227,7 +235,7 @@ pub fn reduce<T>(list []T, reduce_op fn (t1 T, t2 T) T) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// accumulates values with providing initial value and applying providing operation to current accumulator value and each elements.
|
// accumulates values with providing initial value and applying providing operation to current accumulator value and each elements.
|
||||||
// usage: `arrays.fold<string, byte>(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] })` => `149`
|
// Example: arrays.fold<string, byte>(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] }) // => 149
|
||||||
pub fn fold<T, R>(list []T, init R, fold_op fn (r R, t T) R) R {
|
pub fn fold<T, R>(list []T, init R, fold_op fn (r R, t T) R) R {
|
||||||
mut value := init
|
mut value := init
|
||||||
|
|
||||||
|
@ -239,7 +247,7 @@ pub fn fold<T, R>(list []T, init R, fold_op fn (r R, t T) R) R {
|
||||||
}
|
}
|
||||||
|
|
||||||
// flattens n + 1 dimensional array into n dimensional array
|
// flattens n + 1 dimensional array into n dimensional array
|
||||||
// usage: `arrays.flatten<int>([[1, 2, 3], [4, 5]])` => `[1, 2, 3, 4, 5]`
|
// Example: arrays.flatten<int>([[1, 2, 3], [4, 5]]) // => [1, 2, 3, 4, 5]
|
||||||
pub fn flatten<T>(list [][]T) []T {
|
pub fn flatten<T>(list [][]T) []T {
|
||||||
// calculate required capacity
|
// calculate required capacity
|
||||||
mut required_size := 0
|
mut required_size := 0
|
||||||
|
@ -263,7 +271,7 @@ pub fn flatten<T>(list [][]T) []T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// grouping list of elements with given key selector.
|
// grouping list of elements with given key selector.
|
||||||
// usage: `arrays.assort<int, string>(['H', 'el', 'lo'], fn (v string) int { return v.len })` => `{1: ['H'], 2: ['el', 'lo']}`
|
// Example: arrays.group_by<int, string>(['H', 'el', 'lo'], fn (v string) int { return v.len }) // => {1: ['H'], 2: ['el', 'lo']}
|
||||||
pub fn group_by<K, V>(list []V, grouping_op fn (v V) K) map[K][]V {
|
pub fn group_by<K, V>(list []V, grouping_op fn (v V) K) map[K][]V {
|
||||||
mut result := map[K][]V{}
|
mut result := map[K][]V{}
|
||||||
|
|
||||||
|
@ -281,7 +289,13 @@ pub fn group_by<K, V>(list []V, grouping_op fn (v V) K) map[K][]V {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// concatenate two arrays
|
// concatenate an array with an arbitrary number of additional values
|
||||||
|
//
|
||||||
|
// NOTE: if you have two arrays, you should simply use the `<<` operator directly
|
||||||
|
// Example: arrays.concat([1, 2, 3], 4, 5, 6) == [1, 2, 3, 4, 5, 6] // => true
|
||||||
|
// Example: arrays.concat([1, 2, 3], ...[4, 5, 6]) == [1, 2, 3, 4, 5, 6] // => true
|
||||||
|
// Example: arr << [4, 5, 6] // does what you need if arr is mutable
|
||||||
|
[deprecated]
|
||||||
pub fn concat<T>(a []T, b ...T) []T {
|
pub fn concat<T>(a []T, b ...T) []T {
|
||||||
mut m := []T{cap: a.len + b.len}
|
mut m := []T{cap: a.len + b.len}
|
||||||
|
|
||||||
|
@ -291,7 +305,32 @@ pub fn concat<T>(a []T, b ...T) []T {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// zip returns a new array by interleaving the source arrays.
|
||||||
|
//
|
||||||
|
// NOTE: The two arrays do not need to be equal sizes
|
||||||
|
// Example: arrays.zip([1, 3, 5, 7], [2, 4, 6, 8, 10]) // => [1, 2, 3, 4, 5, 6, 7, 8, 10]
|
||||||
|
pub fn zip<T>(a []T, b []T) []T {
|
||||||
|
mut m := []T{cap: a.len + b.len}
|
||||||
|
mut i := 0
|
||||||
|
for i < m.cap {
|
||||||
|
// short-circuit the rest of the loop as soon as we can
|
||||||
|
if i >= a.len {
|
||||||
|
m << b[i..]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i >= b.len {
|
||||||
|
m << a[i..]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m << a[i]
|
||||||
|
m << b[i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
// returns the smallest element >= val, requires `arr` to be sorted
|
// returns the smallest element >= val, requires `arr` to be sorted
|
||||||
|
// Example: arrays.lower_bound([2, 4, 6, 8], 3)? // => 4
|
||||||
pub fn lower_bound<T>(arr []T, val T) ?T {
|
pub fn lower_bound<T>(arr []T, val T) ?T {
|
||||||
if arr.len == 0 {
|
if arr.len == 0 {
|
||||||
return error('.lower_bound called on an empty array')
|
return error('.lower_bound called on an empty array')
|
||||||
|
@ -314,6 +353,7 @@ pub fn lower_bound<T>(arr []T, val T) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the largest element <= val, requires `arr` to be sorted
|
// returns the largest element <= val, requires `arr` to be sorted
|
||||||
|
// Example: arrays.upper_bound([2, 4, 6, 8], 3)? // => 2
|
||||||
pub fn upper_bound<T>(arr []T, val T) ?T {
|
pub fn upper_bound<T>(arr []T, val T) ?T {
|
||||||
if arr.len == 0 {
|
if arr.len == 0 {
|
||||||
return error('.upper_bound called on an empty array')
|
return error('.upper_bound called on an empty array')
|
||||||
|
@ -335,7 +375,10 @@ pub fn upper_bound<T>(arr []T, val T) ?T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// binary search, requires `arr` to be sorted, returns index
|
// binary search, requires `arr` to be sorted, returns index of found item or error.
|
||||||
|
// Binary searches on sorted lists can be faster than other array searches because at maximum
|
||||||
|
// the algorithm only has to traverse log N elements
|
||||||
|
// Example: arrays.binary_search([1, 2, 3, 4], 4) ? // => 3
|
||||||
pub fn binary_search<T>(arr []T, target T) ?int {
|
pub fn binary_search<T>(arr []T, target T) ?int {
|
||||||
mut left := 0
|
mut left := 0
|
||||||
mut right := arr.len - 1
|
mut right := arr.len - 1
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
## Description:
|
## Description:
|
||||||
|
|
||||||
`benchmark` provides an easy way to measure how fast a piece of code is,
|
`benchmark` provides tools for measuring and reporting on the performance of code.
|
||||||
and produce a readable report about it.
|
|
||||||
|
|
||||||
## Example usage of this module:
|
## Example 1:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
import benchmark
|
import benchmark
|
||||||
|
@ -25,12 +24,13 @@ bmark.stop()
|
||||||
println(bmark.total_message('remarks about the benchmark'))
|
println(bmark.total_message('remarks about the benchmark'))
|
||||||
```
|
```
|
||||||
|
|
||||||
benchmark.start() and b.measure() are convenience methods,
|
`.start()` and `.measure()` are convenience methods,
|
||||||
intended to be used in combination. Their goal is to make
|
intended to be used in combination. Their goal is to make
|
||||||
benchmarking of small snippets of code as *short*, easy to
|
benchmarking of small snippets of code as _short_, easy to
|
||||||
write, and then to read and analyze the results, as possible.
|
write, and easy to read and analyze as possible.
|
||||||
|
|
||||||
|
## Example 2:
|
||||||
|
|
||||||
Example:
|
|
||||||
```v
|
```v
|
||||||
import time
|
import time
|
||||||
import benchmark
|
import benchmark
|
||||||
|
@ -45,6 +45,7 @@ b.measure('code_2')
|
||||||
```
|
```
|
||||||
|
|
||||||
... which will produce on stdout something like this:
|
... which will produce on stdout something like this:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
SPENT 1500.063 ms in code_1
|
SPENT 1500.063 ms in code_1
|
||||||
SPENT 500.061 ms in code_2
|
SPENT 500.061 ms in code_2
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
## Description:
|
## Description:
|
||||||
|
|
||||||
`bitfield` is a module for manipulating arrays of bits, i.e. series of zeroes
|
`bitfield` is a module for manipulating arrays of bits,
|
||||||
and ones spread across an array of storage units (unsigned 32-bit integers).
|
i.e. series of zeroes and ones spread across an
|
||||||
|
array of storage units (unsigned 32-bit integers).
|
||||||
|
|
||||||
## BitField structure
|
## BitField Structure
|
||||||
|
|
||||||
Bit arrays are stored in data structures called 'BitField'. The structure is
|
Bit arrays are stored in data structures called 'BitField'. The structure is
|
||||||
'opaque', i.e. its internals are not available to the end user. This module
|
'opaque', i.e. its internals are not available to the end user. This module
|
||||||
|
|
|
@ -4,4 +4,3 @@
|
||||||
It implements the builtin V types `array`, `string`, `map`.
|
It implements the builtin V types `array`, `string`, `map`.
|
||||||
It also implements builtin functions like `println`, `eprintln`, `malloc`,
|
It also implements builtin functions like `println`, `eprintln`, `malloc`,
|
||||||
`panic`, `print_backtrace`.
|
`panic`, `print_backtrace`.
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ declarative subcommands, each having separate set of options.
|
||||||
See also the `flag` module, for a simpler command line option parser,
|
See also the `flag` module, for a simpler command line option parser,
|
||||||
that supports only options.
|
that supports only options.
|
||||||
|
|
||||||
Usage example:
|
## Example:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
module main
|
module main
|
||||||
|
|
|
@ -3,3 +3,14 @@
|
||||||
`clipboard` provides access to the platform's clipboard mechanism.
|
`clipboard` provides access to the platform's clipboard mechanism.
|
||||||
You can use it to read from the system clipboard, and write to it
|
You can use it to read from the system clipboard, and write to it
|
||||||
from your applications.
|
from your applications.
|
||||||
|
|
||||||
|
## Examples:
|
||||||
|
|
||||||
|
```v
|
||||||
|
import clipboard
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut c := clipboard.new()
|
||||||
|
println(c.get_text())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn (mut cb Clipboard) clear_all() {
|
||||||
cb.clear()
|
cb.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy destroys the clipboard and free it's resources.
|
// destroy destroys the clipboard and frees its resources.
|
||||||
pub fn (mut cb Clipboard) destroy() {
|
pub fn (mut cb Clipboard) destroy() {
|
||||||
cb.free()
|
cb.free()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
## Description:
|
## Description:
|
||||||
|
|
||||||
`compress.zlib` implements zlib compression and decompression of binary data.
|
`compress.zlib` is a module that assists in the compression and
|
||||||
|
decompression of binary data using `zlib` compression
|
||||||
|
|
||||||
## Examples:
|
## Examples:
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ pub const max_size = u64(1 << 31)
|
||||||
fn C.tdefl_compress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
|
fn C.tdefl_compress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
|
||||||
fn C.tinfl_decompress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
|
fn C.tinfl_decompress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
|
||||||
|
|
||||||
|
// compresses an array of bytes using zlib and returns the compressed bytes in a new array
|
||||||
|
// Example: compressed := zlib.compress(b) ?
|
||||||
[manualfree]
|
[manualfree]
|
||||||
pub fn compress(data []byte) ?[]byte {
|
pub fn compress(data []byte) ?[]byte {
|
||||||
if u64(data.len) > zlib.max_size {
|
if u64(data.len) > zlib.max_size {
|
||||||
|
@ -33,6 +35,8 @@ pub fn compress(data []byte) ?[]byte {
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
|
||||||
|
// Example: decompressed := zlib.decompress(b) ?
|
||||||
[manualfree]
|
[manualfree]
|
||||||
pub fn decompress(data []byte) ?[]byte {
|
pub fn decompress(data []byte) ?[]byte {
|
||||||
mut out_len := usize(0)
|
mut out_len := usize(0)
|
||||||
|
|
|
@ -1,5 +1,45 @@
|
||||||
## Description:
|
## Description:
|
||||||
|
|
||||||
`crypto` is a namespace for multiple crypto algorithms.
|
`crypto` is a module that exposes cryptographic algorithms to V programs.
|
||||||
|
|
||||||
|
Each submodule implements things differently, so be sure to consider the documentation
|
||||||
|
of the specific algorithm you need, but in general, the method is to create a `cipher`
|
||||||
|
struct using one of the module functions, and then to call the `encrypt` or `decrypt`
|
||||||
|
method on that struct to actually encrypt or decrypt your data.
|
||||||
|
|
||||||
|
This module is a work-in-progress. For example, the AES implementation currently requires you
|
||||||
|
to create a destination buffer of the correct size to receive the decrypted data, and the AesCipher
|
||||||
|
`encrypt` and `decrypt` functions only operate on the first block of the `src`.
|
||||||
|
|
||||||
The implementations here are loosely based on [Go's crypto package](https://pkg.go.dev/crypto).
|
The implementations here are loosely based on [Go's crypto package](https://pkg.go.dev/crypto).
|
||||||
|
|
||||||
|
## Examples:
|
||||||
|
|
||||||
|
```v
|
||||||
|
import crypto.aes
|
||||||
|
import crypto.rand
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// remember to save this key somewhere if you ever want to decrypt your data
|
||||||
|
key := rand.read(32) ?
|
||||||
|
println('KEY: $key')
|
||||||
|
|
||||||
|
// this data is one block (16 bytes) big
|
||||||
|
mut data := 'THIS IS THE DATA'.bytes()
|
||||||
|
|
||||||
|
println('generating cipher')
|
||||||
|
cipher := aes.new_cipher(key)
|
||||||
|
|
||||||
|
println('performing encryption')
|
||||||
|
mut encrypted := []byte{len: aes.block_size}
|
||||||
|
cipher.encrypt(mut encrypted, mut data)
|
||||||
|
println(encrypted)
|
||||||
|
|
||||||
|
println('performing decryption')
|
||||||
|
mut decrypted := []byte{len: aes.block_size}
|
||||||
|
cipher.decrypt(mut decrypted, mut encrypted)
|
||||||
|
println(decrypted)
|
||||||
|
|
||||||
|
assert decrypted == data
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -13,13 +13,16 @@ pub const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AesCipher represents an AES encryption using a particular key.
|
// AesCipher represents an AES encryption using a particular key.
|
||||||
|
// It follows the API of golang's `cipher.Block` and is designed to
|
||||||
|
// handle only one block of data at a time. In most cases, you
|
||||||
|
// probably want to encrypt and decrypt using [[AesCbc](#AesCbc)]
|
||||||
struct AesCipher {
|
struct AesCipher {
|
||||||
mut:
|
mut:
|
||||||
enc []u32
|
enc []u32
|
||||||
dec []u32
|
dec []u32
|
||||||
}
|
}
|
||||||
|
|
||||||
// new_cipher creates and returns a new `AesCipher`.
|
// new_cipher creates and returns a new [[AesCipher](#AesCipher)].
|
||||||
// The key argument should be the AES key,
|
// The key argument should be the AES key,
|
||||||
// either 16, 24, or 32 bytes to select
|
// either 16, 24, or 32 bytes to select
|
||||||
// AES-128, AES-192, or AES-256.
|
// AES-128, AES-192, or AES-256.
|
||||||
|
@ -43,8 +46,10 @@ pub fn (c &AesCipher) block_size() int {
|
||||||
return aes.block_size
|
return aes.block_size
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt encrypts the blocks in `src` to `dst`.
|
// encrypt encrypts the first block of data in `src` to `dst`.
|
||||||
// Please note: `dst` and `src` are both mutable for performance reasons.
|
// NOTE: `dst` and `src` are both mutable for performance reasons.
|
||||||
|
// NOTE: `dst` and `src` must both be pre-allocated to the correct length.
|
||||||
|
// NOTE: `dst` and `src` may be the same (overlapping entirely).
|
||||||
pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) {
|
pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) {
|
||||||
if src.len < aes.block_size {
|
if src.len < aes.block_size {
|
||||||
panic('crypto.aes: input not full block')
|
panic('crypto.aes: input not full block')
|
||||||
|
@ -60,8 +65,10 @@ pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) {
|
||||||
encrypt_block_generic(c.enc, mut dst, src)
|
encrypt_block_generic(c.enc, mut dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt decrypts the blocks in `src` to `dst`.
|
// decrypt decrypts the first block of data in `src` to `dst`.
|
||||||
// Please note: `dst` and `src` are both mutable for performance reasons.
|
// NOTE: `dst` and `src` are both mutable for performance reasons.
|
||||||
|
// NOTE: `dst` and `src` must both be pre-allocated to the correct length.
|
||||||
|
// NOTE: `dst` and `src` may be the same (overlapping entirely).
|
||||||
pub fn (c &AesCipher) decrypt(mut dst []byte, mut src []byte) {
|
pub fn (c &AesCipher) decrypt(mut dst []byte, mut src []byte) {
|
||||||
if src.len < aes.block_size {
|
if src.len < aes.block_size {
|
||||||
panic('crypto.aes: input not full block')
|
panic('crypto.aes: input not full block')
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
Based on Go's crypto packages.
|
|
||||||
|
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -4,3 +4,54 @@
|
||||||
which in turn is a library of "Simple STB-style cross-platform libraries
|
which in turn is a library of "Simple STB-style cross-platform libraries
|
||||||
for C and C++, written in C.", that provide access to graphics/audio/input
|
for C and C++, written in C.", that provide access to graphics/audio/input
|
||||||
processing.
|
processing.
|
||||||
|
|
||||||
|
Each `.h` file in the sokol source code is well-documented as can be seen here:
|
||||||
|
|
||||||
|
[sokol_audio.h](https://github.com/floooh/sokol/blob/master/sokol_audio.h)
|
||||||
|
|
||||||
|
## Example from `@VROOTDIR/examples/sokol/sounds/simple_sin_tones.v`:
|
||||||
|
|
||||||
|
```v
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import sokol.audio
|
||||||
|
|
||||||
|
const (
|
||||||
|
sw = time.new_stopwatch()
|
||||||
|
sw_start_ms = sw.elapsed().milliseconds()
|
||||||
|
)
|
||||||
|
|
||||||
|
[inline]
|
||||||
|
fn sintone(periods int, frame int, num_frames int) f32 {
|
||||||
|
return math.sinf(f32(periods) * (2 * math.pi) * f32(frame) / f32(num_frames))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn my_audio_stream_callback(buffer &f32, num_frames int, num_channels int) {
|
||||||
|
ms := sw.elapsed().milliseconds() - sw_start_ms
|
||||||
|
unsafe {
|
||||||
|
mut soundbuffer := buffer
|
||||||
|
for frame := 0; frame < num_frames; frame++ {
|
||||||
|
for ch := 0; ch < num_channels; ch++ {
|
||||||
|
idx := frame * num_channels + ch
|
||||||
|
if ms < 250 {
|
||||||
|
soundbuffer[idx] = 0.5 * sintone(20, frame, num_frames)
|
||||||
|
} else if ms < 300 {
|
||||||
|
soundbuffer[idx] = 0.5 * sintone(25, frame, num_frames)
|
||||||
|
} else if ms < 1500 {
|
||||||
|
soundbuffer[idx] *= sintone(22, frame, num_frames)
|
||||||
|
} else {
|
||||||
|
soundbuffer[idx] = 0.5 * sintone(25, frame, num_frames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
audio.setup(
|
||||||
|
stream_cb: my_audio_stream_callback
|
||||||
|
)
|
||||||
|
time.sleep(2000 * time.millisecond)
|
||||||
|
audio.shutdown()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -12,9 +12,37 @@ $if linux {
|
||||||
#flag darwin -framework AudioToolbox
|
#flag darwin -framework AudioToolbox
|
||||||
#flag windows -lole32
|
#flag windows -lole32
|
||||||
|
|
||||||
|
// callback function for `stream_cb` in [[C.saudio_desc](#C.saudio_desc)] when calling [audio.setup()](#setup)
|
||||||
//
|
//
|
||||||
|
// sokol callback functions run in a separate thread
|
||||||
|
//
|
||||||
|
// This function will be called with a reference to the C buffer and the maximum number of frames and channels
|
||||||
|
// the audio backend is expecting in its buffer.
|
||||||
|
//
|
||||||
|
// Terms:
|
||||||
|
// - *sample* - a 32-bit floating point number from `-1.0` to `+1.0` representing the waveform amplitude at that instant
|
||||||
|
// - *frame* - one sample for each channel at that instant
|
||||||
|
//
|
||||||
|
// To determine the number of samples expected, do `num_frames * num_channels`.
|
||||||
|
// Then, write up to that many `f32` samples into `buffer` using unsafe operations.
|
||||||
|
//
|
||||||
|
// Do not write more data to the buffer than it is requesting, but you may write less. The buffer is initialized with
|
||||||
|
// zeroes, so unwritten data will result in audio silence.
|
||||||
|
// Example: unsafe { C.memcpy(buffer, &samples, samples.len * int(sizeof(f32))) }
|
||||||
|
// Example: unsafe { mut b := buffer; for i, sample in samples { b[i] = sample } }
|
||||||
pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int)
|
pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int)
|
||||||
|
|
||||||
|
// callback function for `stream_userdata_cb` to use in `C.saudio_desc` when calling [audio.setup()](#setup)
|
||||||
|
//
|
||||||
|
// sokol callback functions run in a separate thread
|
||||||
|
//
|
||||||
|
// This function operates the same way as [[FNStreamingCB](#FNStreamingCB)] but it passes customizable `user_data` to the
|
||||||
|
// callback. This is the method to use if your audio data is stored in a struct or array. Identify the
|
||||||
|
// `user_data` when you call `audio.setup()` and that object will be passed to the callback as the last arg.
|
||||||
|
// Example: mut soundbuffer := []f32
|
||||||
|
// Example: soundbuffer << previously_parsed_wavfile_bytes
|
||||||
|
// Example: audio.setup(stream_userdata_cb: mycallback, user_data: soundbuffer)
|
||||||
|
// Example: fn mycallback(buffer &f32, num_frames int, num_channels int, mut sb []f32) { ... }
|
||||||
pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr)
|
pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr)
|
||||||
|
|
||||||
pub fn (x FNStreamingCB) str() string {
|
pub fn (x FNStreamingCB) str() string {
|
||||||
|
@ -25,7 +53,17 @@ pub fn (x FnStreamingCBWithUserData) str() string {
|
||||||
return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }'
|
return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only one of `stream_cb` or `stream_userdata_cb` should be used
|
||||||
//
|
//
|
||||||
|
// default values (internal to sokol C library):
|
||||||
|
//
|
||||||
|
// | variable | default | note |
|
||||||
|
// | :----------- | -------: | :--------- |
|
||||||
|
// | sample_rate | 44100 | higher sample rates take more memory but are higher quality |
|
||||||
|
// | num_channels | 1 | for stereo sound, this should be 2 |
|
||||||
|
// | buffer_frames | 2048 | buffer size in frames, larger is more latency, smaller means higher CPU |
|
||||||
|
// | packet_frames | 128 | push model only, number of frames that will be pushed in each packet |
|
||||||
|
// | num_packets | 64 | for push model only, number of packets in the backend ringbuffer |
|
||||||
pub struct C.saudio_desc {
|
pub struct C.saudio_desc {
|
||||||
sample_rate int
|
sample_rate int
|
||||||
num_channels int
|
num_channels int
|
||||||
|
@ -84,17 +122,17 @@ pub fn query() C.saudio_desc {
|
||||||
return C.saudio_query_desc()
|
return C.saudio_query_desc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// audio.sample_rate - actual sample rate
|
// audio.sample_rate - return the actual sample rate
|
||||||
pub fn sample_rate() int {
|
pub fn sample_rate() int {
|
||||||
return C.saudio_sample_rate()
|
return C.saudio_sample_rate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// audio.buffer_frames - return actual backend buffer size in number of frames
|
// audio.buffer_frames - return the actual backend buffer size in number of frames
|
||||||
pub fn buffer_frames() int {
|
pub fn buffer_frames() int {
|
||||||
return C.saudio_buffer_frames()
|
return C.saudio_buffer_frames()
|
||||||
}
|
}
|
||||||
|
|
||||||
// audio.channels - actual number of channels
|
// audio.channels - return the actual number of channels
|
||||||
pub fn channels() int {
|
pub fn channels() int {
|
||||||
return C.saudio_channels()
|
return C.saudio_channels()
|
||||||
}
|
}
|
||||||
|
@ -105,7 +143,7 @@ pub fn suspended() bool {
|
||||||
return C.saudio_suspended()
|
return C.saudio_suspended()
|
||||||
}
|
}
|
||||||
|
|
||||||
// audio.expect - get current number of frames to fill packet queue; use in combination with audio.push/2
|
// audio.expect - get current number of frames to fill packet queue; use in combination with audio.push
|
||||||
pub fn expect() int {
|
pub fn expect() int {
|
||||||
return C.saudio_expect()
|
return C.saudio_expect()
|
||||||
}
|
}
|
||||||
|
@ -115,7 +153,8 @@ pub fn push(frames &f32, num_frames int) int {
|
||||||
return C.saudio_push(frames, num_frames)
|
return C.saudio_push(frames, num_frames)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// audio.fclamp - helper function to 'clamp' a number to a certain range
|
||||||
|
// Example: realsample := audio.fclamp(sample, -1.0, 1.0)
|
||||||
[inline]
|
[inline]
|
||||||
pub fn fclamp(x f32, flo f32, fhi f32) f32 {
|
pub fn fclamp(x f32, flo f32, fhi f32) f32 {
|
||||||
if x > fhi {
|
if x > fhi {
|
||||||
|
@ -127,6 +166,10 @@ pub fn fclamp(x f32, flo f32, fhi f32) f32 {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// audio.min - helper function to return the smaller of two numbers
|
||||||
|
//
|
||||||
|
// math.min returns `f32` values, this returns `int` values
|
||||||
|
// Example: smaller := audio.min(1, 5) // smaller == 1
|
||||||
pub fn min(x int, y int) int {
|
pub fn min(x int, y int) int {
|
||||||
if x < y {
|
if x < y {
|
||||||
return x
|
return x
|
||||||
|
@ -134,6 +177,10 @@ pub fn min(x int, y int) int {
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// audio.max - helper function to return the larger of two numbers
|
||||||
|
//
|
||||||
|
// math.max returns `f32` values, this returns `int` values
|
||||||
|
// Example: larger := audio.max(1, 5) // larger == 5
|
||||||
pub fn max(x int, y int) int {
|
pub fn max(x int, y int) int {
|
||||||
if x < y {
|
if x < y {
|
||||||
return y
|
return y
|
||||||
|
|
Loading…
Reference in New Issue