diff options
| author | Shulhan <ms@kilabit.info> | 2022-07-03 15:07:14 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2022-07-03 15:07:19 +0700 |
| commit | ec8dcf9eba1fc69a3b3369a2494ee8f01ef4f196 (patch) | |
| tree | a08f077941332e8f735b04e7f8b08c578eb125f9 | |
| parent | 557cf80f27208fad7bf240ed400185e62d0684e8 (diff) | |
| download | pakakeh.go-0.39.0.tar.xz | |
Release share v0.39.0 (2022-07-03)v0.39.0
Breaking changes
* all: move lib/sanitize.HTML to net/html.Sanitize
New features,
* lib/mlog: add method Close to MultiLogger
* lib/clise: implement json.Marshaler on Clise
* net/html: add function NormalizeForID
* lib/http: add function to unmarshal url.Values using tag `form:`
* lib/reflect: implement Set function to set reflect.Value by string
* lib/reflect: add function Unmarshal
* lib/reflect: add function Tag to simplify lookup on struct's field tag
Enhancements,
* lib/memfs: update the template format
* net/html: use inline replacement to clean up white spaces
* lib/mlog: minimize allocation when generating log
* lib/dns: use Shutdown to stop DoH server
* lib/websocket: realign all struct to minimize allocations
| -rw-r--r-- | CHANGELOG.adoc | 230 | ||||
| -rw-r--r-- | _doc/CHANGELOG.html | 396 | ||||
| -rw-r--r-- | share.go | 2 |
3 files changed, 620 insertions, 8 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 0d8835a6..7a09d5ee 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,6 +1,6 @@ = CHANGELOG Shulhan <ms@kilabit.info> -31 May 2022 +3 July 2022 :toc: :sectanchors: :sectlinks: @@ -17,6 +17,234 @@ link:CHANGELOG_2018-2019.html[Changelog from 2018 to 2019^]. This is changelog for share module since v0.1.0 until v0.11.0. +[#v0_39_0] +== share v0.39.0 (2022-07-03) + +[#v0_39_0_breaking_changes] +== Breaking changes + +all: move lib/sanitize.HTML to net/html.Sanitize:: ++ +-- +Since the sanitize package only contains HTML function, and the html +package already exist, we move the function into html package. +-- + +[#v0_39_0_new_features] +== New features + +lib/mlog: add method Close to MultiLogger:: ++ +-- +The Close method flush and close all log forwarders. +Any write to a closed MultiLogger will be ignored. + +This changes require adding sync.Mutex to mark if the instance has been +closed or not; which affect createMultiLogger and defaultMLog to return +a pointer to prevent copy on Mutex. +-- + +lib/clise: implement json.Marshaler on Clise:: ++ +-- +The MarshalJSON method convert the Clise into slice by calling Slice +and then convert it into JSON. +-- + +lib/reflect: add function Marshal:: ++ +-- +The Marshal function marshal the obj value to []byte by calling one of +the method: MarshalBinary, MarshalJSON, or MarshalText; in respective +order. + +If obj implement one of the method with valid signature, it will return +(out, nil, true); +unless there is an error. + +If the method signature invalid it will return (nil, err, false). + +If obj is nil or none of the method exist it will return +(nil, nil, false). +-- + +net/html: add function NormalizeForID:: ++ +-- +Given an input string, The NormalizeForID normalize it to HTML ID. +The normalization follow Mozilla specification [1] rules, + +* it must not contain whitespace (spaces, tabs etc.), +* only ASCII letters, digits, '_', and '-' should be used, and +* it should start with a letter. + +The NormalizeForID do this normalization, + +* An empty string is equal to "\_". +* Any other unknown characters will be replaced with '\_'. +* If the input does not start with letter, it will be prefixed with + '\_', unless it start with '\_'. +* All letters converted to lower case. + +[1] https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id. +-- + +lib/http: add function to unmarshal url.Values using tag `form:`:: ++ +-- +UnmarshalForm read struct fields tagged with `form:` from out as key and +set its using the value from url.Values based on that key. +If the field does not have `form:` tag but it is exported, then it will +use the field name, in case insensitive. + +Only the following types are supported: bool, int/intX, uint/uintX, +floatX, string, []byte, or type that implement BinaryUnmarshaler +(UnmarshalBinary), json.Unmarshaler (UnmarshalJSON), or TextUnmarshaler +(UnmarshalText). + +A bool type can be set to true using the following string value: "true", +"yes", or "1". + +If the input contains multiple values but the field type is not slice, +the field will be set using the first value. + +It will return an error if the out variable is not set-able (the type is +not a pointer to a struct). +It will not return an error if one of the input value is not match with +field type. +-- + +lib/reflect: implement Set function to set reflect.Value by string:: ++ +-- +The Set function set the obj value by converting the string val from +parameter to the obj type. + +If the obj is an interface or struct, its value will be set by calling +Unmarshal. + +It will return an error if, + +* obj is not setable, variable is passed without pointer or pointer + not initialized. +* val is overflow +* obj Kind is Invalid, Array, Chan, Func, Map, or UnsafePointer. +-- + +lib/reflect: add function Unmarshal:: ++ +-- +The Unmarshal function set the obj value by calling one of the method: +UnmarshalBinary, UnmarshalJSON, or UnmarshalText; in respective +order. + +Just like reflect, the obj value must be pointer to initialized variable +(&T) or pointer-to-pointer to uninitialized variable (**T). + +If obj implement one of the method, it will return (true, nil) if there is +no error. + +If none of the method exist on obj, it will return (false, nil). +-- + +lib/reflect: add function Tag to simplify lookup on struct's field tag:: ++ +-- +Given a StructField and the name of tag, return the tag's value and +options inside the tag. +The options is any string after tag's value, separated by comma. +For example, given the following field definition + + F `tag:"name,opt1, opt2"` + +It will return (name, [opt1 opt2], true). + +If the field is exported but does not have tag, it will return the field +name (as is without converting to lower case) in val with hasTag is +false: (Name, nil, false). + +If the field is un-exported it will return empty val with hasTag is +false ("", nil, false). +-- + +[#v0_39_0_enhancements] +== Enhancements + +lib/memfs: update the template format:: ++ +-- +Replace ":=" with "var" and realign the field assignments. +-- + +net/html: use inline replacement to clean up white spaces:: ++ +-- +Instead of using bytes.Replace, three times, iterate the plain text +manually to clean up the white and multiple spaces. + +Benchmark result, + +---- +name old time/op new time/op delta +Sanitize-8 4.27µs ±10% 2.64µs ±13% -38.21% (p=0.000 n=10+10) + +name old alloc/op new alloc/op delta +Sanitize-8 4.84kB ± 0% 4.45kB ± 0% -7.94% (p=0.000 n=10+10) + +name old allocs/op new allocs/op delta +Sanitize-8 13.0 ± 0% 6.0 ± 0% -53.85% (p=0.000 n=10+10) +---- +-- + +lib/mlog: minimize allocation when generating log:: ++ +-- +Instead of using two bytes.Buffer pool, use one; +and add space after time and prefix by writing to buffer directly instead +of allocating new arguments to Fprintf. + +Benchmark result, + +---- +name old time/op new time/op delta +MultiLogger-8 3.97µs ± 3% 3.68µs ± 2% -7.43% (p=0.008 n=5+5) + +name old alloc/op new alloc/op delta +MultiLogger-8 510B ± 1% 300B ± 1% -41.13% (p=0.008 n=5+5) + +name old allocs/op new allocs/op delta +MultiLogger-8 10.4 ± 6% 3.4 ±18% -67.31% (p=0.008 n=5+5) +---- +-- + +lib/dns: use Shutdown to stop DoH server:: ++ +-- +Using Shutdown allow active connection not interrupted but it may +cause delay when restarting the server. + +While at it, set the doh and dot server instance to nil to release +the resource, in case the Server need to start again. +-- + +lib/websocket: realign all struct to minimize allocations:: ++ +-- +Changes, + +* Client: from 176 to 144 (-32 bytes) +* ClientManager: from 64 to 40 (-24 bytes) +* Frame: from 72 to 56 bytes (-16 bytes). +* Handshak: from 160 to 120 bytes (-40 bytes). +* Request: from 88 to 72 (-16 bytes) +* Response: from 40 to 24 (-16 bytes) +* route: from 48 to 32 (-16 bytes) +* Server: from 72 to 64 (-8 bytes) +* ServerOptions: from 104 to 96 (s-8 bytes) + +Plus other structs in the tests. +-- + [#v0_38_0] == share v0.38.0 (2022-06-05) diff --git a/_doc/CHANGELOG.html b/_doc/CHANGELOG.html index 99c9a887..69d33b43 100644 --- a/_doc/CHANGELOG.html +++ b/_doc/CHANGELOG.html @@ -106,12 +106,11 @@ h3 > span { dl { margin: 1.25rem; } -dd { - margin: 0 0 0 1.25rem; +dt { + font-weight: bold } -dl, dd { - font-size: 0.875rem; + margin: 0 0 0 1.25rem; } /** @@ -240,11 +239,15 @@ dd { <div class="details"> <span id="author" class="author">Shulhan</span><br> <span id="email" class="email"><a href="mailto:ms@kilabit.info">ms@kilabit.info</a></span><br> -<span id="revdate">31 May 2022</span> +<span id="revdate">3 July 2022</span> </div> <div id="toc" class="toc"> <div id="toctitle">Table of Contents</div> <ul class="sectlevel1"> +<li><a href="#v0_39_0">share v0.39.0 (2022-07-03)</a></li> +<li><a href="#v0_39_0_breaking_changes">Breaking changes</a></li> +<li><a href="#v0_39_0_new_features">New features</a></li> +<li><a href="#v0_39_0_enhancements">Enhancements</a></li> <li><a href="#v0_38_0">share v0.38.0 (2022-06-05)</a> <ul class="sectlevel2"> <li><a href="#_breaking_changes">Breaking changes</a></li> @@ -321,6 +324,387 @@ This is changelog for share module since v0.1.0 until v0.11.0.</p> </div> </div> <div class="sect1"> +<h2 id="v0_39_0"><a class="anchor" href="#v0_39_0"></a><a class="link" href="#v0_39_0">share v0.39.0 (2022-07-03)</a></h2> +<div class="sectionbody"> +</div> +</div> +<div class="sect1"> +<h2 id="v0_39_0_breaking_changes"><a class="anchor" href="#v0_39_0_breaking_changes"></a><a class="link" href="#v0_39_0_breaking_changes">Breaking changes</a></h2> +<div class="sectionbody"> +<div class="dlist"> +<dl> +<dt class="hdlist1">all: move lib/sanitize.HTML to net/html.Sanitize</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Since the sanitize package only contains HTML function, and the html +package already exist, we move the function into html package.</p> +</div> +</div> +</div> +</dd> +</dl> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="v0_39_0_new_features"><a class="anchor" href="#v0_39_0_new_features"></a><a class="link" href="#v0_39_0_new_features">New features</a></h2> +<div class="sectionbody"> +<div class="dlist"> +<dl> +<dt class="hdlist1">lib/mlog: add method Close to MultiLogger</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>The Close method flush and close all log forwarders. +Any write to a closed MultiLogger will be ignored.</p> +</div> +<div class="paragraph"> +<p>This changes require adding sync.Mutex to mark if the instance has been +closed or not; which affect createMultiLogger and defaultMLog to return +a pointer to prevent copy on Mutex.</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/clise: implement json.Marshaler on Clise</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>The MarshalJSON method convert the Clise into slice by calling Slice +and then convert it into JSON.</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/reflect: add function Marshal</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>The Marshal function marshal the obj value to []byte by calling one of +the method: MarshalBinary, MarshalJSON, or MarshalText; in respective +order.</p> +</div> +<div class="paragraph"> +<p>If obj implement one of the method with valid signature, it will return +(out, nil, true); +unless there is an error.</p> +</div> +<div class="paragraph"> +<p>If the method signature invalid it will return (nil, err, false).</p> +</div> +<div class="paragraph"> +<p>If obj is nil or none of the method exist it will return +(nil, nil, false).</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">net/html: add function NormalizeForID</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Given an input string, The NormalizeForID normalize it to HTML ID. +The normalization follow Mozilla specification [1] rules,</p> +</div> +<div class="ulist"> +<ul> +<li> +<p>it must not contain whitespace (spaces, tabs etc.),</p> +</li> +<li> +<p>only ASCII letters, digits, '_', and '-' should be used, and</p> +</li> +<li> +<p>it should start with a letter.</p> +</li> +</ul> +</div> +<div class="paragraph"> +<p>The NormalizeForID do this normalization,</p> +</div> +<div class="ulist"> +<ul> +<li> +<p>An empty string is equal to "_".</p> +</li> +<li> +<p>Any other unknown characters will be replaced with '_'.</p> +</li> +<li> +<p>If the input does not start with letter, it will be prefixed with +'_', unless it start with '_'.</p> +</li> +<li> +<p>All letters converted to lower case.</p> +</li> +</ul> +</div> +<div class="paragraph"> +<p>[1] <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id" class="bare">https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id</a>.</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/http: add function to unmarshal url.Values using tag <code>form:</code></dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>UnmarshalForm read struct fields tagged with <code>form:</code> from out as key and +set its using the value from url.Values based on that key. +If the field does not have <code>form:</code> tag but it is exported, then it will +use the field name, in case insensitive.</p> +</div> +<div class="paragraph"> +<p>Only the following types are supported: bool, int/intX, uint/uintX, +floatX, string, []byte, or type that implement BinaryUnmarshaler +(UnmarshalBinary), json.Unmarshaler (UnmarshalJSON), or TextUnmarshaler +(UnmarshalText).</p> +</div> +<div class="paragraph"> +<p>A bool type can be set to true using the following string value: "true", +"yes", or "1".</p> +</div> +<div class="paragraph"> +<p>If the input contains multiple values but the field type is not slice, +the field will be set using the first value.</p> +</div> +<div class="paragraph"> +<p>It will return an error if the out variable is not set-able (the type is +not a pointer to a struct). +It will not return an error if one of the input value is not match with +field type.</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/reflect: implement Set function to set reflect.Value by string</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>The Set function set the obj value by converting the string val from +parameter to the obj type.</p> +</div> +<div class="paragraph"> +<p>If the obj is an interface or struct, its value will be set by calling +Unmarshal.</p> +</div> +<div class="paragraph"> +<p>It will return an error if,</p> +</div> +<div class="ulist"> +<ul> +<li> +<p>obj is not setable, variable is passed without pointer or pointer +not initialized.</p> +</li> +<li> +<p>val is overflow</p> +</li> +<li> +<p>obj Kind is Invalid, Array, Chan, Func, Map, or UnsafePointer.</p> +</li> +</ul> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/reflect: add function Unmarshal</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>The Unmarshal function set the obj value by calling one of the method: +UnmarshalBinary, UnmarshalJSON, or UnmarshalText; in respective +order.</p> +</div> +<div class="paragraph"> +<p>Just like reflect, the obj value must be pointer to initialized variable +(&T) or pointer-to-pointer to uninitialized variable (**T).</p> +</div> +<div class="paragraph"> +<p>If obj implement one of the method, it will return (true, nil) if there is +no error.</p> +</div> +<div class="paragraph"> +<p>If none of the method exist on obj, it will return (false, nil).</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/reflect: add function Tag to simplify lookup on struct’s field tag</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Given a StructField and the name of tag, return the tag’s value and +options inside the tag. +The options is any string after tag’s value, separated by comma. +For example, given the following field definition</p> +</div> +<div class="literalblock"> +<div class="content"> +<pre>F `tag:"name,opt1, opt2"`</pre> +</div> +</div> +<div class="paragraph"> +<p>It will return (name, [opt1 opt2], true).</p> +</div> +<div class="paragraph"> +<p>If the field is exported but does not have tag, it will return the field +name (as is without converting to lower case) in val with hasTag is +false: (Name, nil, false).</p> +</div> +<div class="paragraph"> +<p>If the field is un-exported it will return empty val with hasTag is +false ("", nil, false).</p> +</div> +</div> +</div> +</dd> +</dl> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="v0_39_0_enhancements"><a class="anchor" href="#v0_39_0_enhancements"></a><a class="link" href="#v0_39_0_enhancements">Enhancements</a></h2> +<div class="sectionbody"> +<div class="dlist"> +<dl> +<dt class="hdlist1">lib/memfs: update the template format</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Replace ":=" with "var" and realign the field assignments.</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">net/html: use inline replacement to clean up white spaces</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Instead of using bytes.Replace, three times, iterate the plain text +manually to clean up the white and multiple spaces.</p> +</div> +<div class="paragraph"> +<p>Benchmark result,</p> +</div> +<div class="listingblock"> +<div class="content"> +<pre>name old time/op new time/op delta +Sanitize-8 4.27µs ±10% 2.64µs ±13% -38.21% (p=0.000 n=10+10) + +name old alloc/op new alloc/op delta +Sanitize-8 4.84kB ± 0% 4.45kB ± 0% -7.94% (p=0.000 n=10+10) + +name old allocs/op new allocs/op delta +Sanitize-8 13.0 ± 0% 6.0 ± 0% -53.85% (p=0.000 n=10+10)</pre> +</div> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/mlog: minimize allocation when generating log</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Instead of using two bytes.Buffer pool, use one; +and add space after time and prefix by writing to buffer directly instead +of allocating new arguments to Fprintf.</p> +</div> +<div class="paragraph"> +<p>Benchmark result,</p> +</div> +<div class="listingblock"> +<div class="content"> +<pre>name old time/op new time/op delta +MultiLogger-8 3.97µs ± 3% 3.68µs ± 2% -7.43% (p=0.008 n=5+5) + +name old alloc/op new alloc/op delta +MultiLogger-8 510B ± 1% 300B ± 1% -41.13% (p=0.008 n=5+5) + +name old allocs/op new allocs/op delta +MultiLogger-8 10.4 ± 6% 3.4 ±18% -67.31% (p=0.008 n=5+5)</pre> +</div> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/dns: use Shutdown to stop DoH server</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Using Shutdown allow active connection not interrupted but it may +cause delay when restarting the server.</p> +</div> +<div class="paragraph"> +<p>While at it, set the doh and dot server instance to nil to release +the resource, in case the Server need to start again.</p> +</div> +</div> +</div> +</dd> +<dt class="hdlist1">lib/websocket: realign all struct to minimize allocations</dt> +<dd> +<div class="openblock"> +<div class="content"> +<div class="paragraph"> +<p>Changes,</p> +</div> +<div class="ulist"> +<ul> +<li> +<p>Client: from 176 to 144 (-32 bytes)</p> +</li> +<li> +<p>ClientManager: from 64 to 40 (-24 bytes)</p> +</li> +<li> +<p>Frame: from 72 to 56 bytes (-16 bytes).</p> +</li> +<li> +<p>Handshak: from 160 to 120 bytes (-40 bytes).</p> +</li> +<li> +<p>Request: from 88 to 72 (-16 bytes)</p> +</li> +<li> +<p>Response: from 40 to 24 (-16 bytes)</p> +</li> +<li> +<p>route: from 48 to 32 (-16 bytes)</p> +</li> +<li> +<p>Server: from 72 to 64 (-8 bytes)</p> +</li> +<li> +<p>ServerOptions: from 104 to 96 (s-8 bytes)</p> +</li> +</ul> +</div> +<div class="paragraph"> +<p>Plus other structs in the tests.</p> +</div> +</div> +</div> +</dd> +</dl> +</div> +</div> +</div> +<div class="sect1"> <h2 id="v0_38_0"><a class="anchor" href="#v0_38_0"></a><a class="link" href="#v0_38_0">share v0.38.0 (2022-06-05)</a></h2> <div class="sectionbody"> <div class="paragraph"> @@ -1594,7 +1978,7 @@ at the same times.</p> </div> <div id="footer"> <div id="footer-text"> -Last updated 2022-07-03 14:36:51 +0700 +Last updated 2022-07-03 15:04:34 +0700 </div> </div> </div> @@ -8,5 +8,5 @@ package share const ( // Version of this module. - Version = "0.36.0" + Version = "0.39.0" ) |
