Package immutable provides immutable collection types. Immutable collections provide an efficient, safe way to share collections of data while minimizing locks. The collections in this package provide List, Map, and SortedMap implementations. These act similarly to slices and maps, respectively, except that altering a collection returns a new copy of the collection with that change. Because collections are unable to change, they are safe for multiple goroutines to read from at the same time without a mutex. However, these types of collections come with increased CPU & memory usage as compared with Go's built-in collection types so please evaluate for your specific use. The List type provides an API similar to Go slices. They allow appending, prepending, and updating of elements. Elements can also be fetched by index or iterated over using a ListIterator. The Map & SortedMap types provide an API similar to Go maps. They allow values to be assigned to unique keys and allow for the deletion of keys. Values can be fetched by key and key/value pairs can be iterated over using the appropriate iterator type. Both map types provide the same API. The SortedMap, however, provides iteration over sorted keys while the Map provides iteration over unsorted keys. Maps improved performance and memory usage as compared to SortedMaps. Map types require the use of a Hasher implementation to calculate hashes for their keys and check for key equality. SortedMaps require the use of a Comparer implementation to sort keys in the map. These collection types automatically provide built-in hasher and comparers for int, string, and byte slice keys. If you are using one of these key types then simply pass a nil into the constructor. Otherwise you will need to implement a custom Hasher or Comparer type. Please see the provided implementations for reference.
Package freeze enables the "freezing" of data, similar to JavaScript's Object.freeze(). A frozen object cannot be modified; attempting to do so will result in an unrecoverable panic. Freezing is useful for providing soft guarantees of immutability. That is: the compiler can't prevent you from mutating an frozen object, but the runtime can. One of the unfortunate aspects of Go is its limited support for constants: structs, slices, and even arrays cannot be declared as consts. This becomes a problem when you want to pass a slice around to many consumers without worrying about them modifying it. With freeze, you can guard against these unwanted or intended behaviors. To accomplish this, the mprotect syscall is used. Sadly, this necessitates allocating new memory via mmap and copying the data into it. This performance penalty should not be prohibitive, but it's something to be aware of. In case it wasn't clear from the previous paragraph, this package is not intended to be used in production. A well-designed API is a much saner solution than freezing your data structures. I would even caution against using freeze in your automated testing, due to its platform-specific nature. freeze is best used for "one-off" debugging. Something like this: 1. Observe bug 2. Suspect that shared mutable data is the culprit 3. Call freeze.Object on the data after it is created 4. Run program again; it crashes 5. Inspect stack trace to identify where the data was modified 6. Fix bug 7. Remove call to freeze.Object Again: do not use freeze in production. It's a cool proof-of-concept, and it can be useful for debugging, but that's about it. Let me put it another way: freeze imports four packages: reflect, runtime, unsafe, and syscall (actually golang.org/x/sys/unix). Does that sound like a package you want to depend on? Okay, back to the real documention: Functions are provided for freezing the three "pointer types:" Pointer, Slice, and Map. Each function returns a copy of their input that is backed by protected memory. In addition, Object is provided for freezing recursively. Given a slice of pointers, Object will prevent modifications to both the pointer data and the slice data, while Slice merely does the latter. To freeze an object: Note that since foo does not contain any pointers, calling Pointer(f) would have the same effect here. It is recommended that, where convenient, you reassign the return value to its original variable, as with append. Otherwise, you will retain both the mutable original and the frozen copy. Likewise, to freeze a slice: Interfaces can also be frozen, since internally they are just pointers to objects. The effect of this is that the interface's pure methods can still be called, but impure methods cannot. Unfortunately the impurity of a given method is defined by the implementation, not the interface. Even a String method could conceivably modify some internal state. Furthermore, the caveat about unexported struct fields (see below) applies here, so many exported objects cannot be completely frozen. This package depends heavily on the internal representations of the slice and map types. These objects are not likely to change, but if they do, this package will break. In general, you can't call Object on the same object twice. This is because Object will attempt to rewrite the object's internal pointers -- which is a memory modification. Calling Pointer or Slice twice should be fine. Object cannot descend into unexported struct fields. It can still freeze the field itself, but if the field contains a pointer, the data it points to will not be frozen. Appending to a frozen slice will trigger a panic iff len(slice) < cap(slice). This is because appending to a full slice will allocate new memory. Unix is the only supported platform. Windows support is not planned, because it doesn't support a syscall analogous to mprotect.
Package num provides uint128 (U128) and int128 (I128) 128-bit integer types, covering most of the big.Int API. `I128` is a signed "two's complement" implementation that should behave the same way on overflow as `int64`. `U128` and `I128` are immutable value types by default; operations always return a new value rather than mutating the existing one. Performance on x86-64/amd64 architectures is the focus. Simple example: 64-bit helpers: U128 and I128 can be created from a variety of sources: U128 and I128 support the following formatting and marshalling interfaces:
Package iavl implements a versioned, snapshottable (immutable) AVL+ tree for persisting key-value pairs. The tree is not safe for concurrent use, and must be guarded by a Mutex or RWLock as appropriate - the exception is immutable trees returned by MutableTree.GetImmutable() which are safe for concurrent use as long as the version is not deleted via DeleteVersion(). Basic usage of MutableTree: Proof of existence: Proof of absence: Now we delete an old version: Can't create a proof of absence for a version we no longer have:
Package securecookie is a fast, efficient and safe cookie value encoder/decoder. A secure cookie has its value ciphered and signed with a message authentication code. This prevents the remote cookie owner from knowing what information is stored in the cookie or modifying it. It also prevents an attacker from forging a fake securecookie. What makes this secure cookie package different is that it is fast (faster than the Gorilla secure cookie), and the value encoding and decoding needs no heap allocations. The intended use is to instantiate all secure cookie objects of your web site at program start up. In the following example "session" is the cookie name. The key is a byte sequence generated with the GenerateRandomKey() function. The same key must be used to retrieve the value. An securecookie can also be instantiated as a global variable with the MustNew function. The function will panic if an error occurs. A securecookie object is immutable. It's methods can be called from different goroutines. The Delete(), SetValue() and GetValue() methods are independent. To set a cookie value, call the SetValue() method with a http.ResponseWriter w, and a value val as arguments. A securecookie value is retrieved from an http.Request with the GetValue() method and is appended to the given buffer, extending it if required. If buf is nil, a new buffer is allocated. A securecookie is deleted by calling the Delete() method with the http.ResponseWriter w as argument.
Package hamt provides immutable collection types of maps (associative arrays) and sets implemented as Hash-Array Mapped Tries (HAMTs). All operations of the collections, such as insert and delete, are immutable and create new ones keeping original ones unmodified.
Package boutique provides an immutable state storage with subscriptions to changes in the store. It is intended to be a single storage for individual client data or a single state store for an application not requiring high QPS. Features: Drawbacks: When we say immutable, we mean that everything gets copied, as Go does not have immutable objects or types other than strings. This means every update to a pointer or reference type (map, dict, slice) must make a copy of the data before changing it, not a mutation. Because of modern processors, this copying is quite fast. Boutique provides storage that is best designed in a modular method: The files are best organized by using them as follows: Please see github.com/johnsiilver/boutique for a complete guide to using this package. Its complicated enough to warrant some documentation to guide you through. If your very impatient, there is an example directory with examples of verying complexity.
Package tagset supports creation and manipulation of sets of tags. It does so in a safe and efficient fashion, supporting: - consistent hashing of tagsets to recognize commonalities - flexible combination of tagsets from multiple sources - immutability to allow re-use of tagsets The package otherwise presents a fairly abstract API that allows performance optimizations without changing semantics. HashlessTagsAccumulator and HashingTagsAccumulator both allow building tagsets bit-by-bit, by appending new tags. The HashedTags type represents an _immutable_ set of tags and associated hashes. It is the primary data structure used to represent a set of tags.
Package frozen provides immutable data structures.
Tlogdb is a trivial transparent log client and server. It is meant as more a starting point to be customized than a tool to be used directly. A transparent log is a tamper-proof, append-only, immutable log of data records. That is, if the server were to violate the “append-only, immutable” properties, that tampering would be detected by the client. For more about transparent logs, see https://research.swtch.com/tlog. To create a new log (new server state): The newlog command creates a new database in file (default tlog.db) containing an empty log and a newly generated public/private key pair for the server using the given name. The newlog command prints the newly generated public key. To see it again: To add a record named name to the log: To serve the authenticated log data: The default server address is localhost:6655. The client maintains a cache database both for performance (avoiding duplicate downloads) and for storing the server's public key and the most recently seen log head. To create a new client cache: The newcache command creates a new database in file (default tlogclient.db) and stores the given public key for later use. The key should be the output of the tlogdb's server commands newlog or publickey, described above. To look up a record in the log: The default server address is again localhost:6655. The protocol between client and server is the same as used in the Go module checksum database, documented at https://golang.org/design/25530-sumdb#checksum-database. There are three endpoints: /latest serves a signed tree head; /lookup/NAME looks up the given name, and /tile/* serves log tiles. Putting the various commands together in a Unix shell:
Tlogdb is a trivial transparent log client and server. It is meant as more a starting point to be customized than a tool to be used directly. A transparent log is a tamper-proof, append-only, immutable log of data records. That is, if the server were to violate the “append-only, immutable” properties, that tampering would be detected by the client. For more about transparent logs, see https://research.swtch.com/tlog. To create a new log (new server state): The newlog command creates a new database in file (default tlog.db) containing an empty log and a newly generated public/private key pair for the server using the given name. The newlog command prints the newly generated public key. To see it again: To add a record named name to the log: To serve the authenticated log data: The default server address is localhost:6655. The client maintains a cache database both for performance (avoiding duplicate downloads) and for storing the server's public key and the most recently seen log head. To create a new client cache: The newcache command creates a new database in file (default tlogclient.db) and stores the given public key for later use. The key should be the output of the tlogdb's server commands newlog or publickey, described above. To look up a record in the log: The default server address is again localhost:6655. The protocol between client and server is the same as used in the Go module checksum database, documented at https://golang.org/design/25530-sumdb#checksum-database. There are three endpoints: /latest serves a signed tree head; /lookup/NAME looks up the given name, and /tile/* serves log tiles. Putting the various commands together in a Unix shell:
Package iavl implements a versioned, snapshottable (immutable) AVL+ tree for persisting key-value pairs. Basic usage of MutableTree. Proof of existence: Proof of absence: Now we delete an old version: Can't create a proof of absence for a version we no longer have:
Package disgord provides Go bindings for the documented Discord API, and allows for a stateful Client using the Session interface, with the option of a configurable caching system or bypass the built-in caching logic all together. Create a Disgord session to get access to the REST API and socket functionality. In the following example, we listen for new messages and write a "hello" message when our handler function gets fired. Session interface: https://godoc.org/github.com/andersfylling/disgord/#Session Disgord also provides the option to listen for events using a channel. The setup is exactly the same as registering a function. Simply define your channel, add buffering if you need it, and register it as a handler in the `.On` method. Never close a channel without removing the handler from Disgord. You can't directly call Remove, instead you inject a controller to dictate the handler's lifetime. Since you are the owner of the channel, disgord will never close it for you. Here is what it would look like to use the channel for handling events. Please run this in a go routine unless you know what you are doing. Disgord handles sharding for you automatically; when starting the bot, when discord demands you to scale up your shards (during runtime), etc. It also gives you control over the shard setup in case you want to run multiple instances of Disgord (in these cases you must handle scaling yourself as Disgord can not). Sharding is done behind the scenes, so you do not need to worry about any settings. Disgord will simply ask Discord for the recommended amount of shards for your bot on startup. However, to set specific amount of shards you can use the `disgord.ShardConfig` to specify a range of valid shard IDs (starts from 0). starting a bot with exactly 5 shards Running multiple instances each with 1 shard (note each instance must use unique shard ids) Handle scaling options yourself > Note: if you create a CacheConfig you don't have to set every field. > Note: Only LFU is supported. > Note: Lifetime options does not currently work/do anything (yet). A part of Disgord is the control you have; while this can be a good detail for advanced Users, we recommend beginners to utilise the default configurations (by simply not editing the configuration). Example of configuring the Cache: If you just want to change a specific field, you can do so. The fields are always default values. > Note: Disabling caching for some types while activating it for others (eg. disabling Channels, but activating guild caching), can cause items extracted from the Cache to not reflect the true discord state. Example, activated guild but disabled channel caching: The guild is stored to the Cache, but it's Channels are discarded. Guild Channels are dismantled from the guild object and otherwise stored in the channel Cache to improve performance and reduce memory use. So when you extract the cached guild object, all of the channel will only hold their channel ID, and nothing more. To keep it safe and reliable, you can not directly affect the contents of the Cache. Unlike discordgo where everything is mutable, the caching in disgord is immutable. This does reduce performance as a copy must be made (only on new Cache entries), but as a performance freak, I can tell you right now that a simple struct copy is not that expensive. This also means that, as long as discord sends their events properly, the caching will always reflect the true state of discord. If there is a bug in the Cache and you keep getting the incorrect data, please file an issue at github.com/andersfylling/disgord so it can quickly be resolved(!) Whenever you call a REST method from the Session interface; the Cache is always checked first. Upon a Cache hit, no REST request is executed and you get the data from the Cache in return. However, if this is problematic for you or there exist a bug which gives you bad/outdated data, you can bypass it by using Disgord flags. In addition to disgord.IgnoreCache, as shown above, you can pass in other flags such as: disgord.SortByID, disgord.OrderAscending, etc. You can find these flags in the flag.go file. `disgord_diagnosews` will store all the incoming and outgoing JSON data as files in the directory "diagnose-report/packets". The file format is as follows: unix_clientType_direction_shardID_operationCode_sequenceNumber[_eventName].json `json_std` switches out jsoniter with the json package from the std libs. `disgord_removeDiscordMutex` replaces mutexes in discord structures with a empty mutex; removes locking behaviour and any mutex code when compiled. `disgord_parallelism` activates built-in locking in discord structure methods. Eg. Guild.AddChannel(*Channel) does not do locking by default. But if you find yourself using these discord data structures in parallel environment, you can activate the internal locking to reduce race conditions. Note that activating `disgord_parallelism` and `disgord_removeDiscordMutex` at the same time, will cause you to have no locking as `disgord_removeDiscordMutex` affects the same mutexes. `disgord_legacy` adds wrapper methods with the original discord naming. eg. For REST requests you will notice Disgord uses a consistency between update/create/get/delete/set while discord uses edit/update/modify/close/delete/remove/etc. So if you struggle find a REST method, you can enable this build tag to gain access to mentioned wrappers. `disgordperf` does some low level tweaking that can help boost json unmarshalling and drops json validation from Discord responses/events. Other optimizations might take place as well. `disgord_websocket_gorilla` replaces nhooyr/websocket dependency with gorilla/websocket for gateway communication. In addition to the typical REST endpoints for deleting data, you can also use Client/Session.DeleteFromDiscord(...) for basic deletions. If you need to delete a specific range of messages, or anything complex as that; you can't use .DeleteFromDiscord(...). Not every struct has implemented the interface that allows you to call DeleteFromDiscord. Do not fret, if you try to pass a type that doesn't qualify, you get a compile error.
Package color colorizes your terminal strings. Default Brush are available in sub-package brush for your convenience. You can invoke them directly: ...or you can create new ones! Create a Style, which has convenience methods : Style.WithForeground or WithBackground returns a new Style, with the applied Paint. Styles are immutable so the original one is left unchanged : Style.Brush gives you a Brush that you can invoke directly to colorize strings : You can use it with all sorts of things : That's it!
Package dot implements data synchronization of user defined types using operational transformation/OT. Please see https://github.com/dotchain/dot for a tutorial on how to use DOT. The core functionality is spread out between dot/changes, dot/streams, dot/refs and dot/ops but this package exposes simple client and server implementations for common use cases: Server example Client example DOT uses immutable values. Every Value must implement the change.Value interface which is a single Apply method that returns the result of applying a mutation (while leaving the original value effectively unchanged). If the underlying type behaves like a collection (such as with Slices), the type must also implement some collection specific methods specified in the changes.Collection interface. Most actual types are likely to be structs or slices with boilerplate implementaations of the interfaces. The x/dotc package has a code generator which can emit such boilerplate implementations simplifying this task. The changes package implements a set of simple changes (Replace, Splice and Move). Richer changes are expected to be built up by composition via changes.ChangeSet (which is a sequence of changes) and changes.PathChange (which modifies a value at a path). Changes are immutable too and generally are meant to not maintain any reference to the value they apply on. While custom changes are possible (they have to implement the changes.Custom interface), they are expected to be rare as the default set of chnange types cover a vast variety of scenarios. The core logic of DOT is in the Merge methods of changes: they guaranteee that if two independent changes are done to a value, the deviation in the values can be converged. The basic property of any two changes (on the same value) is that: Care must be taken with custom changes to ensure that this property is preserved. Streams represent the sequence of changes associated with a single value. Stream instances behave like they are immutable: when a change happens, a new stream instance captures the change. Streams also support multiple-writers: it is possible for two independent changes to the same stream instance. In this case, the newly-created stream instances only capture the respective changes but these both have a "Next" value that converges to the same value. That is, the two separate streams implicitly have the changes from each other (but after transforming through the Merge) method. This allows streams to perform quite nicely as convergent data structures without much syntax overhead: The streams package provides a generic Stream implementation (via the New function) which implements the idea of a sequence of convergent changes. But much of the power of streams is in having strongly type streams where the stream is associated with a strongly typed value. The streams package provides simple text streamss (S8 and S16) as well as Bool and Counter types. Richer types like structs and slices can be converted to their stream equivalent rather mechanically and this is done by the x/dotc package -- using code generation. Substreams are streams that refer into a particular field of a parent stream. For example, if the parent value is a struct with a "Done" field, it is possible to treat the "Done stream" as the changes scoped to this field. This allows code to be written much more cleanly. See the https://github.com/dotchain/dot#toggling-complete section of the documentation for an example. Streams support branching (a la Git) and folding. See the examples! Streams also support references. A typical use case is maintaining the user cursor within a region of text. When remote changes happen to the text, the cursor needs to be updated. In fact, when one takes a substream of an element of an array, the array index needs to be automatically managed (i.e. insertions into the array before the index should automatically update the index etc). This is managed within streams using references. A particular value can be reconstituted from the sequence of changes to that value. In DOT, only these changes are stored and that too in an append-only log. This make the backend rather simple and generally agnostic of application types to a large extent. See https://github.com/dotchain/dot#server for example code.