tag:blogger.com,1999:blog-37215967306562314212024-02-18T20:24:00.785-07:00F = T ∇ SτAlexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.comBlogger89125tag:blogger.com,1999:blog-3721596730656231421.post-64823238659843365502021-09-21T13:01:00.002-06:002021-09-21T13:01:45.137-06:00LinuxSys Probe Utility<style>
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment the following line when using as a custom stylesheet */
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
b,strong{font-weight:bold}
abbr{font-size:.9em}
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
dfn{font-style:italic}
hr{height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type=checkbox],input[type=radio]{padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,::before,::after{box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;-webkit-tap-highlight-color:transparent}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos{border-right:1px solid;opacity:.35;padding-right:.5em}
pre.pygments .lineno{border-right:1px solid;opacity:.35;display:inline-block;margin-right:.75em}
pre.pygments .lineno::before{content:"";margin-right:-.125em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]{border-bottom:1px dotted}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge .cm {
color: #999988;
font-style: italic;
}
pre.rouge .cp {
color: #999999;
font-weight: bold;
}
pre.rouge .c1 {
color: #999988;
font-style: italic;
}
pre.rouge .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cpf {
color: #999988;
font-style: italic;
}
pre.rouge .err {
color: #a61717;
background-color: #e3d2d2;
}
pre.rouge .gd {
color: #000000;
background-color: #ffdddd;
}
pre.rouge .ge {
color: #000000;
font-style: italic;
}
pre.rouge .gr {
color: #aa0000;
}
pre.rouge .gh {
color: #999999;
}
pre.rouge .gi {
color: #000000;
background-color: #ddffdd;
}
pre.rouge .go {
color: #888888;
}
pre.rouge .gp {
color: #555555;
}
pre.rouge .gs {
font-weight: bold;
}
pre.rouge .gu {
color: #aaaaaa;
}
pre.rouge .gt {
color: #aa0000;
}
pre.rouge .kc {
color: #000000;
font-weight: bold;
}
pre.rouge .kd {
color: #000000;
font-weight: bold;
}
pre.rouge .kn {
color: #000000;
font-weight: bold;
}
pre.rouge .kp {
color: #000000;
font-weight: bold;
}
pre.rouge .kr {
color: #000000;
font-weight: bold;
}
pre.rouge .kt {
color: #445588;
font-weight: bold;
}
pre.rouge .k, pre.rouge .kv {
color: #000000;
font-weight: bold;
}
pre.rouge .mf {
color: #009999;
}
pre.rouge .mh {
color: #009999;
}
pre.rouge .il {
color: #009999;
}
pre.rouge .mi {
color: #009999;
}
pre.rouge .mo {
color: #009999;
}
pre.rouge .m, pre.rouge .mb, pre.rouge .mx {
color: #009999;
}
pre.rouge .sa {
color: #000000;
font-weight: bold;
}
pre.rouge .sb {
color: #d14;
}
pre.rouge .sc {
color: #d14;
}
pre.rouge .sd {
color: #d14;
}
pre.rouge .s2 {
color: #d14;
}
pre.rouge .se {
color: #d14;
}
pre.rouge .sh {
color: #d14;
}
pre.rouge .si {
color: #d14;
}
pre.rouge .sx {
color: #d14;
}
pre.rouge .sr {
color: #009926;
}
pre.rouge .s1 {
color: #d14;
}
pre.rouge .ss {
color: #990073;
}
pre.rouge .s, pre.rouge .dl {
color: #d14;
}
pre.rouge .na {
color: #008080;
}
pre.rouge .bp {
color: #999999;
}
pre.rouge .nb {
color: #0086B3;
}
pre.rouge .nc {
color: #445588;
font-weight: bold;
}
pre.rouge .no {
color: #008080;
}
pre.rouge .nd {
color: #3c5d5d;
font-weight: bold;
}
pre.rouge .ni {
color: #800080;
}
pre.rouge .ne {
color: #990000;
font-weight: bold;
}
pre.rouge .nf, pre.rouge .fm {
color: #990000;
font-weight: bold;
}
pre.rouge .nl {
color: #990000;
font-weight: bold;
}
pre.rouge .nn {
color: #555555;
}
pre.rouge .nt {
color: #000080;
}
pre.rouge .vc {
color: #008080;
}
pre.rouge .vg {
color: #008080;
}
pre.rouge .vi {
color: #008080;
}
pre.rouge .nv, pre.rouge .vm {
color: #008080;
}
pre.rouge .ow {
color: #000000;
font-weight: bold;
}
pre.rouge .o {
color: #000000;
font-weight: bold;
}
pre.rouge .w {
color: #bbbbbb;
}
pre.rouge {
background-color: #f8f8f8;
}
</style>
<div id="content">
<div class="sectionbody">
<div class="paragraph">
<p>I’ve published a new small <a href="https://gitlab.com/nuald/linuxsys-probe/">GNU/Linux system probe utility</a> to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>read cgroups information (cpusets and memory)</p>
</li>
<li>
<p>read /proc information</p>
</li>
<li>
<p>probe CPU affinity and memory allocation</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The detailed description and usage could be found on the aforementioned link. Let me here describe a couple of use-cases as an intruduction.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_probe_cpu_affinity_and_memory_allocation">Probe CPU affinity and memory allocation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Setting CPU affinity and allocating memory could silently fail (e.g. if cgroups
control them). The utility uses those failures to find the boundaries:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">docker run <span class="nt">-it</span> <span class="nt">--rm</span> <span class="nt">-v</span> <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>:/opt <span class="se">\</span>
<span class="nt">--cpuset-cpus</span><span class="o">=</span><span class="s2">"2-4"</span> <span class="nt">--memory</span><span class="o">=</span><span class="s2">"100m"</span> alpine <span class="se">\</span>
/opt/linuxsys-probe <span class="nt">-d</span> 10MiB <span class="nt">-r</span> probe</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code>probe.cpu::affinity [0] = [*2, 3, 4]
probe.cpu::affinity [1] = [*2, 3, 4]
probe.cpu::affinity [2] = [*2]
probe.cpu::affinity [3] = [*3]
probe.cpu::affinity [4] = [*4]
probe.cpu::affinity [5] = [*2, 3, 4]
probe.cpu::affinity [6] = [2, *3, 4]
probe.cpu::affinity [7] = [2, 3, *4]
probe.cpu::affinity [8] = [2, 3, *4]
probe.cpu::affinity [9] = [2, *3, 4]
probe.cpu::affinity [10] = [*2, 3, 4]
probe.cpu::affinity [11] = [*2, 3, 4]
probe.mem::alloc 4.26 GiB = false
probe.mem::alloc 2.13 GiB = false
probe.mem::alloc 1.07 GiB = false
probe.mem::alloc 545.69 MiB = false
probe.mem::alloc 272.85 MiB = false
probe.mem::alloc 136.42 MiB = false
probe.mem::alloc 68.21 MiB = true
probe.mem::alloc 102.32 MiB = false
probe.mem::alloc 85.26 MiB = true
probe.mem::alloc 93.79 MiB = false</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>*</code> marks the active CPU core during the probe.</p>
</div>
<div class="sect2">
<h3 id="_plot_histogram_from_procfs_values">Plot histogram from procfs values</h3>
<div class="paragraph">
<p>The example uses <a href="https://github.com/glamp/bashplotlib">bashplotlib</a> python package:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">pip <span class="nb">install </span>bashplotlib</code></pre>
</div>
</div>
<div class="paragraph">
<p>First, run the utility in refresh mode (please use Enter to stop it). Assuming
<code>60817</code> is PID of the process we examine:</p>
</div>
<div class="listingblock sh">
<div class="content">
<pre class="rouge highlight"><code>./linuxsys-probe -i 1 -r proc.stat -H -p 60817 | tee results.txt</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code>proc.stat::pid 60817
proc.stat::comm actix-web
proc.stat::state S
proc.stat::ppid 56306
proc.stat::pgrp 60817
proc.stat::session 56291
proc.stat::tty_nr 34816
proc.stat::tpgid 60817
proc.stat::flags 4194304
proc.stat::minflt 573
proc.stat::cminflt 0
proc.stat::majflt 0
proc.stat::cmajflt 0
proc.stat::utime 0
proc.stat::stime 0
proc.stat::cutime 0
proc.stat::cstime 0
proc.stat::priority 20
proc.stat::nice 0
proc.stat::num_threads 14
proc.stat::starttime 172820
proc.stat::vsize 907780096
proc.stat::rss 4407296
...</code></pre>
</div>
</div>
<div class="paragraph">
<p>Next, use <code>hist</code> to draw the histogram (grepping for RSS):</p>
</div>
<div class="listingblock sh">
<div class="content">
<pre class="rouge highlight"><code>grep "rss " results.txt | cut -d' ' -f2 | hist</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code> 41| o
39| o
37| o
35| o
33| o
31| o
28| o
26| o o
24| o o
22| o o
20| o o
18| o o
16| o o
13| o o
11| o o
9| o o
7| o o
5| o o o
3| o o o
1| o o oo
-----------
-----------------------------------
| Summary |
-----------------------------------
| observations: 75 |
| min value: 3801088.000000 |
| mean : 10590999.893333 |
| max value: 23162880.000000 |
-----------------------------------</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The available probes include:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>cgroup.v1</code> - control groups v1</p>
</li>
<li>
<p><code>cgroup.v2</code> - control groups v2</p>
</li>
<li>
<p><code>proc</code> - various procfs information</p>
</li>
<li>
<p>etc</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>And could be run in both host and guest systems (please note that <code>cgroups</code>
require <code>sys</code> and <code>cgroup</code> mounts in the guest system).
The utility could be useful for research/development for the virtualized
platforms (e.g. verify that cgroups are properly enforced and read), and
runtime monitoring. Hope it could help you in your work.</p>
</div>
</div>
</div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-20491132941519910342021-05-13T12:55:00.004-06:002021-05-13T12:55:59.324-06:00Primes generation (algorithms vs real code)<style>
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment @import statement to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em}
pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em}
pre.pygments .lineno::before{content:"";margin-right:-.125em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:50%;border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge .cm {
color: #999988;
font-style: italic;
}
pre.rouge .cp {
color: #999999;
font-weight: bold;
}
pre.rouge .c1 {
color: #999988;
font-style: italic;
}
pre.rouge .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cpf {
color: #999988;
font-style: italic;
}
pre.rouge .err {
color: #a61717;
background-color: #e3d2d2;
}
pre.rouge .gd {
color: #000000;
background-color: #ffdddd;
}
pre.rouge .ge {
color: #000000;
font-style: italic;
}
pre.rouge .gr {
color: #aa0000;
}
pre.rouge .gh {
color: #999999;
}
pre.rouge .gi {
color: #000000;
background-color: #ddffdd;
}
pre.rouge .go {
color: #888888;
}
pre.rouge .gp {
color: #555555;
}
pre.rouge .gs {
font-weight: bold;
}
pre.rouge .gu {
color: #aaaaaa;
}
pre.rouge .gt {
color: #aa0000;
}
pre.rouge .kc {
color: #000000;
font-weight: bold;
}
pre.rouge .kd {
color: #000000;
font-weight: bold;
}
pre.rouge .kn {
color: #000000;
font-weight: bold;
}
pre.rouge .kp {
color: #000000;
font-weight: bold;
}
pre.rouge .kr {
color: #000000;
font-weight: bold;
}
pre.rouge .kt {
color: #445588;
font-weight: bold;
}
pre.rouge .k, pre.rouge .kv {
color: #000000;
font-weight: bold;
}
pre.rouge .mf {
color: #009999;
}
pre.rouge .mh {
color: #009999;
}
pre.rouge .il {
color: #009999;
}
pre.rouge .mi {
color: #009999;
}
pre.rouge .mo {
color: #009999;
}
pre.rouge .m, pre.rouge .mb, pre.rouge .mx {
color: #009999;
}
pre.rouge .sa {
color: #000000;
font-weight: bold;
}
pre.rouge .sb {
color: #d14;
}
pre.rouge .sc {
color: #d14;
}
pre.rouge .sd {
color: #d14;
}
pre.rouge .s2 {
color: #d14;
}
pre.rouge .se {
color: #d14;
}
pre.rouge .sh {
color: #d14;
}
pre.rouge .si {
color: #d14;
}
pre.rouge .sx {
color: #d14;
}
pre.rouge .sr {
color: #009926;
}
pre.rouge .s1 {
color: #d14;
}
pre.rouge .ss {
color: #990073;
}
pre.rouge .s, pre.rouge .dl {
color: #d14;
}
pre.rouge .na {
color: #008080;
}
pre.rouge .bp {
color: #999999;
}
pre.rouge .nb {
color: #0086B3;
}
pre.rouge .nc {
color: #445588;
font-weight: bold;
}
pre.rouge .no {
color: #008080;
}
pre.rouge .nd {
color: #3c5d5d;
font-weight: bold;
}
pre.rouge .ni {
color: #800080;
}
pre.rouge .ne {
color: #990000;
font-weight: bold;
}
pre.rouge .nf, pre.rouge .fm {
color: #990000;
font-weight: bold;
}
pre.rouge .nl {
color: #990000;
font-weight: bold;
}
pre.rouge .nn {
color: #555555;
}
pre.rouge .nt {
color: #000080;
}
pre.rouge .vc {
color: #008080;
}
pre.rouge .vg {
color: #008080;
}
pre.rouge .vi {
color: #008080;
}
pre.rouge .nv, pre.rouge .vm {
color: #008080;
}
pre.rouge .ow {
color: #000000;
font-weight: bold;
}
pre.rouge .o {
color: #000000;
font-weight: bold;
}
pre.rouge .w {
color: #bbbbbb;
}
pre.rouge {
background-color: #f8f8f8;
}
</style>
<div id="content">
<div class="paragraph">
<p>Primes is a quite useful tool and have many applications. They are an essential part
of RSA cryptography, and while elliptic curve algorithms are gaining dominance nowadays,
RSA is still widely used. Plus there are other applications like rolling hash functions, i.e.
assigning an unique prime to each character in the required alphabet and multiplying them
(and implementing rolling by dividing to the number corresponding to the previous character,
and multiplying to the number corresponding to the new character).</p>
</div>
<div class="paragraph">
<p>There are several algorithms (or even families of algorithms) to calculate primes,
and recently I’ve stumbled upon a new one:
<a href="https://doi.org/10.1016/j.eij.2020.05.002">A new explicit algorithmic method for generating the prime numbers in order</a>.
It’s been looking promising, and I’ve programmed it, but unfortunately it wasn’t fast enough
as the real code. Authors compared it with sieves of Eratosthenes and Sundaram using their unified framework,
and got nice results.</p>
</div>
<div class="paragraph">
<p>However the implementations do not necessarily fit the frameworks. As actual software
developers we want to have fast code now, that works on the real computers with
all their limitations. And that’s a reason why nice algorithms are not always
the best, e.g. algorithms utilizing linked lists could be not faster
than using vectors/arrays as lists could lead to memory fragmentation and CPU cache misses.</p>
</div>
<div class="paragraph">
<p>Nevertheless, I’ve decided to make a performance comparison, and would like to present to
you the code snippets and the numbers I’ve got. First, let me describe the algorithms
I’ve used:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Proposed Algorithm (based on the aforementioned article). No optimizations are done except
breaking the loop when the generated prime index is more than the limit.</p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve of Eratosthenes</a>. Used as is.</p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Sieve_of_Sundaram">Sieve of Sundaram</a>. Used as is.</p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Sieve_of_Atkin">Sieve of Atkin</a>. Optimized to avoid
additional calculations (please see details in <a class="bare" href="https://www.geeksforgeeks.org/sieve-of-atkin/">https://www.geeksforgeeks.org/sieve-of-atkin/</a>)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The performance results are presented on the image below. Sieve of Atkin is a winner (however,
I have to admit that first I’ve tried the implementation presented in the original paper,
and it was quite slow).</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_ZNLlb0VKSUEIFuuAi4lt-a2jGwQH-__cxnb4gXpMi7FjXd2vdgLsrbHhT10kS96kv91jnUYOCfqCfNlqVsnuk4gbnaKy3fYO4TT4-EY5r1egVf2RhbbPmp6NXWl7q3hB8IxVxSWU_8A/s1398/primes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="964" data-original-width="1398" height="441" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_ZNLlb0VKSUEIFuuAi4lt-a2jGwQH-__cxnb4gXpMi7FjXd2vdgLsrbHhT10kS96kv91jnUYOCfqCfNlqVsnuk4gbnaKy3fYO4TT4-EY5r1egVf2RhbbPmp6NXWl7q3hB8IxVxSWU_8A/w640-h441/primes.png" width="640" /></a></div><br /> <p></p>
</div>
<div class="paragraph">
<p>Each implementation consist of several parts:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>allocating specialized <code>std::vector<bool></code> container for the intermediate data
(memory consumption is O(N) for all algorithms);</p>
</li>
<li>
<p>run the actual algorithm;</p>
</li>
<li>
<p>create the final <code>std::vector<int></code> with the list of primes (the code is similar
for each implementation, thus none of them got an advantage here).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Proposed Algorithm:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">proposed_algo</span><span class="p">(</span><span class="kt">int</span> <span class="n">limit</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">bool</span><span class="o">></span> <span class="n">prime</span><span class="p">(</span><span class="n">limit</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">1</span> <span class="o">+</span> <span class="p">(</span><span class="n">limit</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="n">prime</span><span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">auto</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">const</span> <span class="k">auto</span> <span class="n">sqr</span> <span class="o">=</span> <span class="p">[](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">i</span> <span class="o">*</span> <span class="n">i</span><span class="p">;</span> <span class="p">};</span>
<span class="k">const</span> <span class="k">auto</span> <span class="n">max_n</span> <span class="o">=</span> <span class="p">[</span><span class="n">limit</span><span class="p">,</span> <span class="n">sqr</span><span class="p">](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="n">limit</span> <span class="o">-</span> <span class="n">sqr</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="o">/</span> <span class="p">(</span><span class="mi">4</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">);</span>
<span class="p">};</span>
<span class="k">while</span> <span class="p">(</span><span class="n">k</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">k</span> <span class="o">=</span> <span class="n">max_n</span><span class="p">(</span><span class="n">j</span><span class="o">++</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">k</span> <span class="o">=</span> <span class="n">j</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">auto</span> <span class="n">d</span> <span class="o">=</span> <span class="n">max_n</span><span class="p">(</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">n</span> <span class="o"><</span> <span class="n">d</span><span class="p">;</span> <span class="o">++</span><span class="n">n</span><span class="p">)</span> <span class="p">{</span>
<span class="k">auto</span> <span class="n">index</span> <span class="o">=</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">></span> <span class="n">limit</span><span class="p">)</span> <span class="p">{</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">prime</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">result</span><span class="p">({</span><span class="mi">2</span><span class="p">});</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="n">p</span> <span class="o"><=</span> <span class="n">limit</span><span class="p">;</span> <span class="o">++</span><span class="n">p</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prime</span><span class="p">[</span><span class="n">p</span><span class="p">])</span> <span class="p">{</span>
<span class="n">result</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Sieve of Eratosthenes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">sieve_of_eratosthenes</span><span class="p">(</span><span class="kt">int</span> <span class="n">limit</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">bool</span><span class="o">></span> <span class="n">prime</span><span class="p">(</span><span class="n">limit</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="n">p</span> <span class="o">*</span> <span class="n">p</span> <span class="o"><=</span> <span class="n">limit</span><span class="p">;</span> <span class="o">++</span><span class="n">p</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prime</span><span class="p">[</span><span class="n">p</span><span class="p">])</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="n">p</span> <span class="o">*</span> <span class="n">p</span><span class="p">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="n">limit</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="n">p</span><span class="p">)</span> <span class="p">{</span>
<span class="n">prime</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">result</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="n">p</span> <span class="o"><=</span> <span class="n">limit</span><span class="p">;</span> <span class="o">++</span><span class="n">p</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prime</span><span class="p">[</span><span class="n">p</span><span class="p">])</span> <span class="p">{</span>
<span class="n">result</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Sieve of Sundaram:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">sieve_of_sundaram</span><span class="p">(</span><span class="kt">int</span> <span class="n">limit</span><span class="p">)</span> <span class="p">{</span>
<span class="k">const</span> <span class="k">auto</span> <span class="n">k</span> <span class="o">=</span> <span class="p">(</span><span class="n">limit</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">bool</span><span class="o">></span> <span class="n">marked</span><span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1l</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span> <span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="n">j</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">*</span> <span class="n">j</span><span class="p">)</span> <span class="o"><=</span> <span class="n">k</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span> <span class="p">{</span>
<span class="n">marked</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">j</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">*</span> <span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">result</span><span class="p">({</span><span class="mi">2</span><span class="p">});</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">marked</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span>
<span class="n">result</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Sieve of Atkin:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">sieve_of_atkin</span><span class="p">(</span><span class="kt">int</span> <span class="n">limit</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">bool</span><span class="o">></span> <span class="n">prime</span><span class="p">(</span><span class="n">limit</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span> <span class="o"><</span> <span class="n">limit</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">y</span> <span class="o">*</span> <span class="n">y</span> <span class="o"><</span> <span class="n">limit</span><span class="p">;</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span> <span class="p">{</span>
<span class="k">auto</span> <span class="n">n</span> <span class="o">=</span> <span class="p">(</span><span class="mi">4</span> <span class="o">*</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">y</span> <span class="o">*</span> <span class="n">y</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o"><=</span> <span class="n">limit</span> <span class="o">&&</span> <span class="p">(</span><span class="n">n</span> <span class="o">%</span> <span class="mi">12</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">12</span> <span class="o">==</span> <span class="mi">5</span><span class="p">))</span> <span class="p">{</span>
<span class="n">prime</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="o">!</span><span class="n">prime</span><span class="p">[</span><span class="n">n</span><span class="p">];</span>
<span class="p">}</span>
<span class="n">n</span> <span class="o">=</span> <span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">y</span> <span class="o">*</span> <span class="n">y</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o"><=</span> <span class="n">limit</span> <span class="o">&&</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">12</span> <span class="o">==</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
<span class="n">prime</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="o">!</span><span class="n">prime</span><span class="p">[</span><span class="n">n</span><span class="p">];</span>
<span class="p">}</span>
<span class="n">n</span> <span class="o">=</span> <span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">y</span> <span class="o">*</span> <span class="n">y</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">></span> <span class="n">y</span> <span class="o">&&</span> <span class="n">n</span> <span class="o"><=</span> <span class="n">limit</span> <span class="o">&&</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">12</span> <span class="o">==</span> <span class="mi">11</span><span class="p">)</span> <span class="p">{</span>
<span class="n">prime</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="o">!</span><span class="n">prime</span><span class="p">[</span><span class="n">n</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="n">r</span> <span class="o">*</span> <span class="n">r</span> <span class="o"><</span> <span class="n">limit</span><span class="p">;</span> <span class="o">++</span><span class="n">r</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prime</span><span class="p">[</span><span class="n">r</span><span class="p">])</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="n">r</span> <span class="o">*</span> <span class="n">r</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">limit</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="n">r</span> <span class="o">*</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
<span class="n">prime</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">result</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">});</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="n">p</span> <span class="o"><=</span> <span class="n">limit</span><span class="p">;</span> <span class="o">++</span><span class="n">p</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prime</span><span class="p">[</span><span class="n">p</span><span class="p">])</span> <span class="p">{</span>
<span class="n">result</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>As you could see, algorithm run time complexity doesn’t necessarily directly correlate to its
implementation performance. Please keep the open mind, and verify the algorithms
using the real code (at least on PoC level). Happy hacking!</p>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2021-05-13 12:51:46 -0600
</div>
</div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-63108187809735018702021-04-26T23:58:00.005-06:002021-05-02T12:52:16.461-06:00Testing with multi-arch Docker images<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment @import statement to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em}
pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em}
pre.pygments .lineno::before{content:"";margin-right:-.125em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:50%;border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge .cm {
color: #999988;
font-style: italic;
}
pre.rouge .cp {
color: #999999;
font-weight: bold;
}
pre.rouge .c1 {
color: #999988;
font-style: italic;
}
pre.rouge .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cpf {
color: #999988;
font-style: italic;
}
pre.rouge .err {
color: #a61717;
background-color: #e3d2d2;
}
pre.rouge .gd {
color: #000000;
background-color: #ffdddd;
}
pre.rouge .ge {
color: #000000;
font-style: italic;
}
pre.rouge .gr {
color: #aa0000;
}
pre.rouge .gh {
color: #999999;
}
pre.rouge .gi {
color: #000000;
background-color: #ddffdd;
}
pre.rouge .go {
color: #888888;
}
pre.rouge .gp {
color: #555555;
}
pre.rouge .gs {
font-weight: bold;
}
pre.rouge .gu {
color: #aaaaaa;
}
pre.rouge .gt {
color: #aa0000;
}
pre.rouge .kc {
color: #000000;
font-weight: bold;
}
pre.rouge .kd {
color: #000000;
font-weight: bold;
}
pre.rouge .kn {
color: #000000;
font-weight: bold;
}
pre.rouge .kp {
color: #000000;
font-weight: bold;
}
pre.rouge .kr {
color: #000000;
font-weight: bold;
}
pre.rouge .kt {
color: #445588;
font-weight: bold;
}
pre.rouge .k, pre.rouge .kv {
color: #000000;
font-weight: bold;
}
pre.rouge .mf {
color: #009999;
}
pre.rouge .mh {
color: #009999;
}
pre.rouge .il {
color: #009999;
}
pre.rouge .mi {
color: #009999;
}
pre.rouge .mo {
color: #009999;
}
pre.rouge .m, pre.rouge .mb, pre.rouge .mx {
color: #009999;
}
pre.rouge .sa {
color: #000000;
font-weight: bold;
}
pre.rouge .sb {
color: #d14;
}
pre.rouge .sc {
color: #d14;
}
pre.rouge .sd {
color: #d14;
}
pre.rouge .s2 {
color: #d14;
}
pre.rouge .se {
color: #d14;
}
pre.rouge .sh {
color: #d14;
}
pre.rouge .si {
color: #d14;
}
pre.rouge .sx {
color: #d14;
}
pre.rouge .sr {
color: #009926;
}
pre.rouge .s1 {
color: #d14;
}
pre.rouge .ss {
color: #990073;
}
pre.rouge .s, pre.rouge .dl {
color: #d14;
}
pre.rouge .na {
color: #008080;
}
pre.rouge .bp {
color: #999999;
}
pre.rouge .nb {
color: #0086B3;
}
pre.rouge .nc {
color: #445588;
font-weight: bold;
}
pre.rouge .no {
color: #008080;
}
pre.rouge .nd {
color: #3c5d5d;
font-weight: bold;
}
pre.rouge .ni {
color: #800080;
}
pre.rouge .ne {
color: #990000;
font-weight: bold;
}
pre.rouge .nf, pre.rouge .fm {
color: #990000;
font-weight: bold;
}
pre.rouge .nl {
color: #990000;
font-weight: bold;
}
pre.rouge .nn {
color: #555555;
}
pre.rouge .nt {
color: #000080;
}
pre.rouge .vc {
color: #008080;
}
pre.rouge .vg {
color: #008080;
}
pre.rouge .vi {
color: #008080;
}
pre.rouge .nv, pre.rouge .vm {
color: #008080;
}
pre.rouge .ow {
color: #000000;
font-weight: bold;
}
pre.rouge .o {
color: #000000;
font-weight: bold;
}
pre.rouge .w {
color: #bbbbbb;
}
pre.rouge {
background-color: #f8f8f8;
}
</style>
</head>
<body class="article">
<div id="header">
<h1>Testing with multi-arch Docker images</h1>
</div>
<div id="content">
<div class="paragraph">
<p>With the introduction of <a href="https://docs.docker.com/buildx/working-with-buildx/">Docker Buildx</a>
CLI plugin it became quite easy to test various platforms using single Dockerfile
without any QEMU static builds. In this tutorial we’re going to
use a multistage build with the simple C++ program that tests features
of various platforms.</p>
</div>
<div class="paragraph">
<p>First, we need to register QEMU handlers. Please note that the handler registration won’t survive a reboot.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span>docker run <span class="nt">--rm</span> <span class="nt">--privileged</span> docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64</code></pre>
</div>
</div>
<div class="paragraph">
<p>There is no <code>latest</code> alias for the <code>docker/binfmt</code> and it’s recommended to grab the proper
tag on their homepage: <a href="https://hub.docker.com/r/docker/binfmt/tags?page=1&ordering=last_updated" class="bare">https://hub.docker.com/r/docker/binfmt/tags?page=1&ordering=last_updated</a></p>
</div>
<div class="paragraph">
<p>The code to detect the platform endianness and its sizes for the basic types:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="cp">#include <iostream>
</span>
<span class="cp">#ifndef PLATFORM
</span> <span class="cp">#define PLATFORM "N/A"
#endif
</span>
<span class="kt">void</span> <span class="nf">platform</span><span class="p">()</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"Platform: "</span> <span class="o"><<</span> <span class="n">PLATFORM</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#ifdef __BYTE_ORDER__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__BYTE_ORDER__</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="n">__BYTE_ORDER__</span> <span class="o"><<</span> <span class="s">" ("</span><span class="p">;</span>
<span class="cp">#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_BIG_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_LITTLE_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"unknown"</span><span class="p">;</span>
<span class="cp">#endif
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">")"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__BYTE_ORDER__ is undefined"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#endif
</span>
<span class="cp">#ifdef __FLOAT_WORD_ORDER__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__FLOAT_WORD_ORDER__</span><span class="se">\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="n">__FLOAT_WORD_ORDER__</span> <span class="o"><<</span> <span class="s">" ("</span><span class="p">;</span>
<span class="cp">#if __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_BIG_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#elif __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_LITTLE_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"unknown"</span><span class="p">;</span>
<span class="cp">#endif
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">")"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__FLOAT_WORD_ORDER__ is undefined"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#endif
</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(bool)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">bool</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">bool</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(char)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(wchar_t)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">wchar_t</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">wchar_t</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(short)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">short</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">short</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(int)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(long)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(long long)</span><span class="se">\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span> <span class="kt">long</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span> <span class="kt">long</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(float)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">float</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">float</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(double)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(void*)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">platform</span><span class="p">();</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The Dockerfile uses multistage build to compile and run the program:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="docker"><span class="k">FROM</span><span class="s"> alpine AS builder</span>
<span class="k">RUN </span>apk add g++
<span class="k">WORKDIR</span><span class="s"> /home</span>
<span class="k">COPY</span><span class="s"> platform.C .</span>
<span class="c"># TARGETPLATFORM is provided by Buildx</span>
<span class="k">ARG</span><span class="s"> TARGETPLATFORM</span>
<span class="k">RUN </span>g++ <span class="s2">"-DPLATFORM=</span><span class="se">\"</span><span class="nv">$TARGETPLATFORM</span><span class="se">\"</span><span class="s2">"</span> platform.C <span class="nt">-o</span> platform <span class="nt">-static</span>
<span class="k">FROM</span><span class="s"> alpine</span>
<span class="k">WORKDIR</span><span class="s"> /home</span>
<span class="k">COPY</span><span class="s"> --from=builder /home/platform .</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["./platform"]</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>buildx</code> does support batch build, but only within the limited output options (like <code>--push</code>).
For local testing, I’d prefer to avoid pushing, and rather build the image for the local Docker.
Unfortunately, that won’t work in the batch mode, therefore some shell scripting is required.</p>
</div>
<div class="paragraph">
<p>Let’s prepare all the required files first (within some temporary directory):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="nb">mkdir </span>multiarch
<span class="nv">$ </span><span class="nb">cd </span>multiarch
<span class="nv">$ </span>curl <span class="nt">-O</span> https://gitlab.com/nuald/nuald-blogspot-com/-/raw/master/site/adoc/platform.C
<span class="nv">$ </span>curl <span class="nt">-o</span> Dockerfile https://gitlab.com/nuald/nuald-blogspot-com/-/raw/master/site/adoc/multiarch-dockerfile</code></pre>
</div>
</div>
<div class="paragraph">
<p>Get all supported platforms by the <code>buildx</code> builder:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ PLATFORMS</span><span class="o">=(</span><span class="si">$(</span>docker buildx inspect | <span class="nb">sed</span> <span class="nt">-n</span> <span class="nt">-e</span> <span class="s1">'s/Platforms: \(.*\)/\1/p'</span> | <span class="nb">tr</span> <span class="s1">','</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="si">)</span><span class="o">)</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="k">${</span><span class="nv">PLATFORMS</span><span class="p">[*]</span><span class="k">}</span>
linux/amd64 linux/386 linux/arm64 linux/riscv64 linux/ppc64le linux/s390x linux/arm/v7 linux/arm/v6</code></pre>
</div>
</div>
<div class="paragraph">
<p>Build all images (please note that <code>riscv64</code> platform is not supported by the official Alpine dockerhub):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="k">for </span>platform <span class="k">in</span> <span class="k">${</span><span class="nv">PLATFORMS</span><span class="p">[@]</span><span class="k">}</span><span class="p">;</span> <span class="k">do </span>docker buildx build <span class="nt">--platform</span> <span class="nv">$platform</span> <span class="nt">-o</span> <span class="nb">type</span><span class="o">=</span>docker,name<span class="o">=</span>multiarch/<span class="nv">$platform</span> .<span class="p">;</span> <span class="k">done</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The list of all official supported Docker images for alternative platforms is available in the documentation:
<a href="https://github.com/docker-library/official-images#architectures-other-than-amd64" class="bare">https://github.com/docker-library/official-images#architectures-other-than-amd64</a></p>
</div>
<div class="paragraph">
<p>Finally, run all images (you could see that <code>riscv64</code> image is not available):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="k">for </span>platform <span class="k">in</span> <span class="k">${</span><span class="nv">PLATFORMS</span><span class="p">[@]</span><span class="k">}</span><span class="p">;</span> <span class="k">do </span>docker run <span class="nt">-it</span> <span class="nt">--rm</span> <span class="nt">--platform</span> <span class="nv">$platform</span> multiarch/<span class="nv">$platform</span> .<span class="p">;</span> <span class="k">done
</span>Platform: linux/amd64
__BYTE_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
Platform: linux/386
__BYTE_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
Platform: linux/arm64
__BYTE_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
Unable to find image <span class="s1">'multiarch/linux/riscv64:latest'</span> locally
docker: Error response from daemon: pull access denied <span class="k">for </span>multiarch/linux/riscv64, repository does not exist or may require <span class="s1">'docker login'</span>: denied: requested access to the resource is denied.
See <span class="s1">'docker run --help'</span><span class="nb">.</span>
Platform: linux/ppc64le
__BYTE_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
Platform: linux/s390x
__BYTE_ORDER__ <span class="o">=</span> 4321 <span class="o">(</span>__ORDER_BIG_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 4321 <span class="o">(</span>__ORDER_BIG_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
Platform: linux/arm/v7
__BYTE_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
Platform: linux/arm/v6
__BYTE_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>And surely, it all could be properly automated and integrated with the required CI/CD systems.</p>
</div>
</div>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-18670197862528057152021-04-26T17:00:00.000-06:002021-04-26T17:00:06.709-06:00Testing C++ code on PowerPC with QEMU<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment @import statement to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em}
pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em}
pre.pygments .lineno::before{content:"";margin-right:-.125em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:50%;border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge .cm {
color: #999988;
font-style: italic;
}
pre.rouge .cp {
color: #999999;
font-weight: bold;
}
pre.rouge .c1 {
color: #999988;
font-style: italic;
}
pre.rouge .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cpf {
color: #999988;
font-style: italic;
}
pre.rouge .err {
color: #a61717;
background-color: #e3d2d2;
}
pre.rouge .gd {
color: #000000;
background-color: #ffdddd;
}
pre.rouge .ge {
color: #000000;
font-style: italic;
}
pre.rouge .gr {
color: #aa0000;
}
pre.rouge .gh {
color: #999999;
}
pre.rouge .gi {
color: #000000;
background-color: #ddffdd;
}
pre.rouge .go {
color: #888888;
}
pre.rouge .gp {
color: #555555;
}
pre.rouge .gs {
font-weight: bold;
}
pre.rouge .gu {
color: #aaaaaa;
}
pre.rouge .gt {
color: #aa0000;
}
pre.rouge .kc {
color: #000000;
font-weight: bold;
}
pre.rouge .kd {
color: #000000;
font-weight: bold;
}
pre.rouge .kn {
color: #000000;
font-weight: bold;
}
pre.rouge .kp {
color: #000000;
font-weight: bold;
}
pre.rouge .kr {
color: #000000;
font-weight: bold;
}
pre.rouge .kt {
color: #445588;
font-weight: bold;
}
pre.rouge .k, pre.rouge .kv {
color: #000000;
font-weight: bold;
}
pre.rouge .mf {
color: #009999;
}
pre.rouge .mh {
color: #009999;
}
pre.rouge .il {
color: #009999;
}
pre.rouge .mi {
color: #009999;
}
pre.rouge .mo {
color: #009999;
}
pre.rouge .m, pre.rouge .mb, pre.rouge .mx {
color: #009999;
}
pre.rouge .sa {
color: #000000;
font-weight: bold;
}
pre.rouge .sb {
color: #d14;
}
pre.rouge .sc {
color: #d14;
}
pre.rouge .sd {
color: #d14;
}
pre.rouge .s2 {
color: #d14;
}
pre.rouge .se {
color: #d14;
}
pre.rouge .sh {
color: #d14;
}
pre.rouge .si {
color: #d14;
}
pre.rouge .sx {
color: #d14;
}
pre.rouge .sr {
color: #009926;
}
pre.rouge .s1 {
color: #d14;
}
pre.rouge .ss {
color: #990073;
}
pre.rouge .s, pre.rouge .dl {
color: #d14;
}
pre.rouge .na {
color: #008080;
}
pre.rouge .bp {
color: #999999;
}
pre.rouge .nb {
color: #0086B3;
}
pre.rouge .nc {
color: #445588;
font-weight: bold;
}
pre.rouge .no {
color: #008080;
}
pre.rouge .nd {
color: #3c5d5d;
font-weight: bold;
}
pre.rouge .ni {
color: #800080;
}
pre.rouge .ne {
color: #990000;
font-weight: bold;
}
pre.rouge .nf, pre.rouge .fm {
color: #990000;
font-weight: bold;
}
pre.rouge .nl {
color: #990000;
font-weight: bold;
}
pre.rouge .nn {
color: #555555;
}
pre.rouge .nt {
color: #000080;
}
pre.rouge .vc {
color: #008080;
}
pre.rouge .vg {
color: #008080;
}
pre.rouge .vi {
color: #008080;
}
pre.rouge .nv, pre.rouge .vm {
color: #008080;
}
pre.rouge .ow {
color: #000000;
font-weight: bold;
}
pre.rouge .o {
color: #000000;
font-weight: bold;
}
pre.rouge .w {
color: #bbbbbb;
}
pre.rouge {
background-color: #f8f8f8;
}
</style>
<div id="content">
<div class="paragraph">
<p>PowerPC is not a commonly used platform, but it’s quite useful for testing
proper handling of endianness (as the vast majority of the current modern
platforms is little-endian, but original PPC is big-endian). Let me provide
simple instructions how to test C++ code on the PowerPC platform.</p>
</div>
<div class="paragraph">
<p>First, let’s install all the required dependencies (using Debian as an example):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="nb">sudo </span>apt-get <span class="nb">install </span>qemu-system-ppc g++-powerpc-linux-gnu sshfs</code></pre>
</div>
</div>
<div class="paragraph">
<p>Second, we need to get a proper image for QEMU. There are few QCOW2 ready-to-use
images on <a href="https://people.debian.org/~aurel32/qemu/" class="bare">https://people.debian.org/~aurel32/qemu/</a>. They are quite outdated, but
easy to use, and still helpful for testing. For PPC we’re going to use
standard (headless) Debian Wheezy image:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span>curl <span class="nt">-O</span> https://people.debian.org/~aurel32/qemu/powerpc/debian_wheezy_powerpc_standard.qcow2</code></pre>
</div>
</div>
<div class="paragraph">
<p>We’re going to use SSHFS for data sharing, therefore we’ll start the image with the port forwarding
(binding port 60022 on the host with port 22 on the guest). Surely, if the SSH daemon on the
guest is listening to the different port, the port binding option should be updated accordingly.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span>qemu-system-ppc <span class="nt">-hda</span> debian_wheezy_powerpc_standard.qcow2 <span class="nt">-nic</span> user,hostfwd<span class="o">=</span>tcp::60022-:22</code></pre>
</div>
</div>
<div class="paragraph">
<p>The root credentials: <code>root</code>/<code>root</code>, the user credentials: <code>user</code>/<code>user</code>.</p>
</div>
<div class="paragraph">
<p>Next, mount the user’s home on the guest with some directory on the host:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="nb">mkdir </span>ppc-home
<span class="nv">$ </span>sshfs user@127.0.0.1:/home/user ppc-home <span class="nt">-C</span> <span class="nt">-p</span> 60022</code></pre>
</div>
</div>
<div class="paragraph">
<p>To unmount after you done, please use the corresponding FUSE mount command:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span>fusermount3 <span class="nt">-u</span> ppc-home</code></pre>
</div>
</div>
<div class="paragraph">
<p>Let’s compile and run within the emulated PPC platform the simple program that shows the various
information about the platform.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="cp">#include <iostream>
</span>
<span class="kt">void</span> <span class="nf">platform</span><span class="p">()</span> <span class="p">{</span>
<span class="cp">#ifdef __BYTE_ORDER__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__BYTE_ORDER__</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="n">__BYTE_ORDER__</span> <span class="o"><<</span> <span class="s">" ("</span><span class="p">;</span>
<span class="cp">#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_BIG_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_LITTLE_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"unknown"</span><span class="p">;</span>
<span class="cp">#endif
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">")"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__BYTE_ORDER__ is undefined"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#endif
</span>
<span class="cp">#ifdef __FLOAT_WORD_ORDER__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__FLOAT_WORD_ORDER__</span><span class="se">\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="n">__FLOAT_WORD_ORDER__</span> <span class="o"><<</span> <span class="s">" ("</span><span class="p">;</span>
<span class="cp">#if __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_BIG_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#elif __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__ORDER_LITTLE_ENDIAN__"</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"unknown"</span><span class="p">;</span>
<span class="cp">#endif
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">")"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#else
</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"__FLOAT_WORD_ORDER__ is undefined"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="cp">#endif
</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(bool)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">bool</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">bool</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(char)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(wchar_t)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">wchar_t</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">wchar_t</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(short)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">short</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">short</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(int)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(long)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(long long)</span><span class="se">\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span> <span class="kt">long</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">long</span> <span class="kt">long</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(float)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">float</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">float</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(double)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"sizeof(void*)</span><span class="se">\t\t</span><span class="s">= "</span> <span class="o"><<</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span>
<span class="o"><<</span> <span class="s">" ("</span> <span class="o"><<</span> <span class="mi">8</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" bits)"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">platform</span><span class="p">();</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>We use the cross-compilation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="nb">cd </span>ppc-home
<span class="nv">$ </span>curl <span class="nt">-O</span> https://gitlab.com/nuald/nuald-blogspot-com/-/raw/master/site/adoc/platform.C
<span class="nv">$ </span>powerpc-linux-gnu-g++ platform.C <span class="nt">-o</span> ppc <span class="nt">-static</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>On the guest system, you’ll be able to run the binary now:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="nb">cd</span>
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-a</span> <span class="o">></span> uname.txt
<span class="nv">$ </span>./ppc <span class="o">></span> platform.txt</code></pre>
</div>
</div>
<div class="paragraph">
<p>The generated text file will be available on the host to read:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="nb">cat </span>uname.txt
Linux debian-powerpc 3.2.0-4-powerpc <span class="c">#1 Debian 3.2.51-1 ppc GNU/Linux</span>
<span class="nv">$ </span><span class="nb">cat </span>platform.txt
__BYTE_ORDER__ <span class="o">=</span> 4321 <span class="o">(</span>__ORDER_BIG_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 4321 <span class="o">(</span>__ORDER_BIG_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Please compare with the modern platform:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-a</span>
Linux amber 5.11.16-arch1-1 <span class="c">#1 SMP PREEMPT Wed, 21 Apr 2021 17:22:13 +0000 x86_64 GNU/Linux</span>
<span class="nv">$ </span>g++ platform.C <span class="nt">-o</span> modern <span class="o">&&</span> ./modern
__BYTE_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
__FLOAT_WORD_ORDER__ <span class="o">=</span> 1234 <span class="o">(</span>__ORDER_LITTLE_ENDIAN__<span class="o">)</span>
sizeof<span class="o">(</span>bool<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>char<span class="o">)</span> <span class="o">=</span> 1 <span class="o">(</span>8 bits<span class="o">)</span>
sizeof<span class="o">(</span>wchar_t<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>short<span class="o">)</span> <span class="o">=</span> 2 <span class="o">(</span>16 bits<span class="o">)</span>
sizeof<span class="o">(</span>int<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>long long<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>float<span class="o">)</span> <span class="o">=</span> 4 <span class="o">(</span>32 bits<span class="o">)</span>
sizeof<span class="o">(</span>double<span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span>
sizeof<span class="o">(</span>void<span class="k">*</span><span class="o">)</span> <span class="o">=</span> 8 <span class="o">(</span>64 bits<span class="o">)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>As you could see, PowerPC has different endianess, <code>long</code> and <code>void*</code> sizes (as it’s a 32-bit platform).
While for some languages it won’t make a lot of difference (for example, Java is always big-endian
and use fixed sizes for its primitive types), for C++ it’s quite important and worth testing,
especially if you’re targeting a variety of platforms. Hope it helps you with your projects, and happy hacking.</p>
</div>
</div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-20000657169688442062021-04-14T17:15:00.001-06:002021-04-14T17:15:33.960-06:00CERN’s ROOT Primer (visualization of ping responses data)<style>
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment @import statement to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em}
pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em}
pre.pygments .lineno::before{content:"";margin-right:-.125em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:50%;border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge .cm {
color: #999988;
font-style: italic;
}
pre.rouge .cp {
color: #999999;
font-weight: bold;
}
pre.rouge .c1 {
color: #999988;
font-style: italic;
}
pre.rouge .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cpf {
color: #999988;
font-style: italic;
}
pre.rouge .err {
color: #a61717;
background-color: #e3d2d2;
}
pre.rouge .gd {
color: #000000;
background-color: #ffdddd;
}
pre.rouge .ge {
color: #000000;
font-style: italic;
}
pre.rouge .gr {
color: #aa0000;
}
pre.rouge .gh {
color: #999999;
}
pre.rouge .gi {
color: #000000;
background-color: #ddffdd;
}
pre.rouge .go {
color: #888888;
}
pre.rouge .gp {
color: #555555;
}
pre.rouge .gs {
font-weight: bold;
}
pre.rouge .gu {
color: #aaaaaa;
}
pre.rouge .gt {
color: #aa0000;
}
pre.rouge .kc {
color: #000000;
font-weight: bold;
}
pre.rouge .kd {
color: #000000;
font-weight: bold;
}
pre.rouge .kn {
color: #000000;
font-weight: bold;
}
pre.rouge .kp {
color: #000000;
font-weight: bold;
}
pre.rouge .kr {
color: #000000;
font-weight: bold;
}
pre.rouge .kt {
color: #445588;
font-weight: bold;
}
pre.rouge .k, pre.rouge .kv {
color: #000000;
font-weight: bold;
}
pre.rouge .mf {
color: #009999;
}
pre.rouge .mh {
color: #009999;
}
pre.rouge .il {
color: #009999;
}
pre.rouge .mi {
color: #009999;
}
pre.rouge .mo {
color: #009999;
}
pre.rouge .m, pre.rouge .mb, pre.rouge .mx {
color: #009999;
}
pre.rouge .sa {
color: #000000;
font-weight: bold;
}
pre.rouge .sb {
color: #d14;
}
pre.rouge .sc {
color: #d14;
}
pre.rouge .sd {
color: #d14;
}
pre.rouge .s2 {
color: #d14;
}
pre.rouge .se {
color: #d14;
}
pre.rouge .sh {
color: #d14;
}
pre.rouge .si {
color: #d14;
}
pre.rouge .sx {
color: #d14;
}
pre.rouge .sr {
color: #009926;
}
pre.rouge .s1 {
color: #d14;
}
pre.rouge .ss {
color: #990073;
}
pre.rouge .s, pre.rouge .dl {
color: #d14;
}
pre.rouge .na {
color: #008080;
}
pre.rouge .bp {
color: #999999;
}
pre.rouge .nb {
color: #0086B3;
}
pre.rouge .nc {
color: #445588;
font-weight: bold;
}
pre.rouge .no {
color: #008080;
}
pre.rouge .nd {
color: #3c5d5d;
font-weight: bold;
}
pre.rouge .ni {
color: #800080;
}
pre.rouge .ne {
color: #990000;
font-weight: bold;
}
pre.rouge .nf, pre.rouge .fm {
color: #990000;
font-weight: bold;
}
pre.rouge .nl {
color: #990000;
font-weight: bold;
}
pre.rouge .nn {
color: #555555;
}
pre.rouge .nt {
color: #000080;
}
pre.rouge .vc {
color: #008080;
}
pre.rouge .vg {
color: #008080;
}
pre.rouge .vi {
color: #008080;
}
pre.rouge .nv, pre.rouge .vm {
color: #008080;
}
pre.rouge .ow {
color: #000000;
font-weight: bold;
}
pre.rouge .o {
color: #000000;
font-weight: bold;
}
pre.rouge .w {
color: #bbbbbb;
}
pre.rouge {
background-color: #f8f8f8;
}
</style>
<div class="paragraph">
<p><a href="https://root.cern/">ROOT</a> is a framework for data processing, born at CERN,
at the heart of the research on high-energy physics. While Python-based data
processing framewords are quite trendy now, their performance could be lacking,
plus ROOT could be used within a C++ application directly. It has other
interesting features too, but in this post we’re going to explore the basic
usage: drawing a graph and a boxplot for the ping response values
(measured directly in C++ code using <a href="https://www.boost.org/libs/asio">Boost.Asio</a>).</p>
</div>
<div class="paragraph">
<p>The full source in available as <a class="bare" href="https://gitlab.com/nuald/nuald-blogspot-com/-/blob/master/site/adoc/ping_stats.C">https://gitlab.com/nuald/nuald-blogspot-com/-/blob/master/site/adoc/ping_stats.C</a></p>
</div>
<div class="paragraph">
<p>Pinging code is straightforward and Boost.Asio documentation could explain
all unfamiliar functionality. The high level flow is:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Create <code>io_context</code> that would wait until we hit <code>limit</code> of requests/responses;</p>
</li>
<li>
<p>Start sending requests and receiving responses;</p>
</li>
<li>
<p>During send operation we create <code>echo</code> ICMP header and send it with the 5 seconds timeout (and start sending again in 1 second);</p>
</li>
<li>
<p>During receive operation we verify identifier and sequence number of the response, and print the results (in same format as usual <code>ping</code> utility). After that, we start receiving again.</p>
</li>
<li>
<p>As soon as we get the sequence number equals <code>limit</code>, we stop sending/receiving.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Now, let’s analyze the results using ROOT. Please install ROOT using their <a href="https://root.cern/install/">instructions</a>. Usually it’s available in the package manager, e.g. for Arch Linux:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$ sudo pacman -Syu root</pre>
</div>
</div>
<div class="paragraph">
<p>You can download and start the code above directly (requires <code>sudo</code> in GNU/Linux):</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$ sudo root ping_stats.C</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghhi_5oYs6NDsT7_T2iAciHYdPuNr5dD0cNfTHKhPbcpeyKOb3L83eF6uFqk__oWkxaXcAfQvpWYEUf5PXTLtI6Z2HHqKWbnHmdQf17lD2nXpJkSYy2h34LnET2yRBIaBJhrK0Q6WrJCI/s567/ping.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="387" data-original-width="567" height="435" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghhi_5oYs6NDsT7_T2iAciHYdPuNr5dD0cNfTHKhPbcpeyKOb3L83eF6uFqk__oWkxaXcAfQvpWYEUf5PXTLtI6Z2HHqKWbnHmdQf17lD2nXpJkSYy2h34LnET2yRBIaBJhrK0Q6WrJCI/w640-h435/ping.png" width="640" /></a></div>
</div>
</div>
<div class="paragraph">
<p>Or you can start ROOT interactive shell (please note that Jupyter has it as a <a href="https://github.com/root-project/root/tree/master/bindings/jupyroot">kernel</a> too):</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$ sudo root</pre>
</div>
</div>
<div class="paragraph">
<p>(Please note that <code>sudo</code> is required just for the ping functionality, usually you don’t need it).</p>
</div>
<div class="paragraph">
<p>In ROOT’s prompt we can load (but don’t execute) the code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>root [0] .L ping_stats.C</pre>
</div>
</div>
<div class="paragraph">
<p>Let’s ping OpenDNS (Cisco) server 10 times:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">auto</span> <span class="n">samples</span> <span class="o">=</span> <span class="mi">10</span>
<span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="mi">10</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="k">auto</span> <span class="n">values</span> <span class="o">=</span> <span class="n">get_stats</span><span class="p">(</span><span class="s">"208.67.222.222"</span><span class="p">,</span> <span class="n">samples</span><span class="p">);</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">24</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">24</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">24</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">32</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">24</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">22</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">7</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">25</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">24</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">22</span>
<span class="mi">20</span> <span class="n">bytes</span> <span class="n">from</span> <span class="mf">208.67.222.222</span><span class="o">:</span> <span class="n">icmp_seq</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">ttl</span><span class="o">=</span><span class="mi">58</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="mi">27</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Please note that if you don’t use a semicolon, the result of the expression
is printed immediately. You can examine the values in the interactive shell anytime though:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="n">values</span>
<span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">allocator</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="o">></span> <span class="o">&</span><span class="p">)</span> <span class="p">{</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">27</span> <span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Next, proceed to the drawing. First, we’ll set up the global style palette
(we get the colors from the palette using <code>plc</code> option, and default
palette is not always good enough):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="n">gStyle</span><span class="o">-></span><span class="n">SetPalette</span><span class="p">(</span><span class="n">kSolar</span><span class="p">)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Next, we create the canvas we’re going to draw on (ROOT could create the default
one too):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="k">auto</span> <span class="n">c</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">TCanvas</span><span class="p">();</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>It will immediately show the white empty canvas window. Next, we create
the graph (we use <code>std::iota</code> to create the range ticks for the X axis):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="n">Int_t</span><span class="o">></span> <span class="n">range</span><span class="p">(</span><span class="n">samples</span><span class="p">)</span>
<span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="n">Int_t</span><span class="o">></span> <span class="o">&</span><span class="p">)</span> <span class="p">{</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span> <span class="p">}</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="n">std</span><span class="o">::</span><span class="n">iota</span><span class="p">(</span><span class="n">range</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">range</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="k">auto</span> <span class="n">gr</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">TGraph</span><span class="p">(</span><span class="n">samples</span><span class="p">,</span> <span class="o">&</span><span class="n">range</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&</span><span class="n">values</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Finally, let’s draw the graph (<code>al</code> options mean to draw axis and polyline for the graph, please see the full reference <a href="https://root.cern/doc/master/classTGraphPainter.html#GP01">here</a>):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">9</span><span class="p">]</span> <span class="n">gr</span><span class="o">-></span><span class="n">Draw</span><span class="p">(</span><span class="s">"al plc"</span><span class="p">)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You could see how the canvas has changed and has a graph now. Let’s save it
to SVG:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="n">c</span><span class="o">-></span><span class="n">Print</span><span class="p">(</span><span class="s">"graph.svg"</span><span class="p">)</span>
<span class="n">Info</span> <span class="n">in</span> <span class="o"><</span><span class="n">TCanvas</span><span class="o">::</span><span class="n">Print</span><span class="o">>:</span> <span class="n">SVG</span> <span class="n">file</span> <span class="n">graph</span><span class="p">.</span><span class="n">svg</span> <span class="n">has</span> <span class="n">been</span> <span class="n">created</span></code></pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjF3UDEhE005EUEbY1-VzdD3XzeXKiBlnoTQK5GSC_PXaC24SpXTMwa1sWd8HglhIVBSArFNpz3ZlFezM5EcRqijd3IV10XCWwheoKYTcQZ0uTJyzNgUxHNXAcpOQUSGSu4qajBIyBI1lQ/s567/graph.png" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="600" data-original-height="387" data-original-width="567" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjF3UDEhE005EUEbY1-VzdD3XzeXKiBlnoTQK5GSC_PXaC24SpXTMwa1sWd8HglhIVBSArFNpz3ZlFezM5EcRqijd3IV10XCWwheoKYTcQZ0uTJyzNgUxHNXAcpOQUSGSu4qajBIyBI1lQ/s600/graph.png"/></a></div>
</div>
</div>
<div class="paragraph">
<p>Next, let’s draw a boxplot. It’s a little bit tricky as ROOT consider it as
a special shape of the histogram, so the code could be not so obvious. There
are two parts of it (at least, in the current code):</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Calculate the range of the values in the resulted boxplot;</p>
</li>
<li>
<p>Draw the boxplot itself.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The range is calculated as <code>[Q1 - 1.5 * IQR, Q3 + 1.5 * IQR]</code>, but we’re going
to use another histogram for that, not pure math solution:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="k">auto</span> <span class="n">total_min</span> <span class="o">=</span> <span class="o">*</span><span class="n">std</span><span class="o">::</span><span class="n">min_element</span><span class="p">(</span><span class="n">values</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">values</span><span class="p">.</span><span class="n">end</span><span class="p">())</span>
<span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="mi">22</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">12</span><span class="p">]</span> <span class="k">auto</span> <span class="n">total_max</span> <span class="o">=</span> <span class="o">*</span><span class="n">std</span><span class="o">::</span><span class="n">max_element</span><span class="p">(</span><span class="n">values</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">values</span><span class="p">.</span><span class="n">end</span><span class="p">())</span>
<span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="mi">32</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">13</span><span class="p">]</span> <span class="k">auto</span> <span class="n">h2</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">TH1I</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span> <span class="n">total_max</span> <span class="o">-</span> <span class="n">total_min</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">total_min</span><span class="p">,</span> <span class="n">total_max</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">14</span><span class="p">]</span> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">el</span><span class="o">:</span> <span class="n">values</span><span class="p">)</span> <span class="p">{</span> <span class="n">h2</span><span class="o">-></span><span class="n">Fill</span><span class="p">(</span><span class="n">el</span><span class="p">);</span> <span class="p">}</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">15</span><span class="p">]</span> <span class="n">Double_t</span> <span class="n">q</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">0.</span><span class="p">,</span> <span class="mi">0</span><span class="p">,}</span>
<span class="p">(</span><span class="n">Double_t</span> <span class="p">[</span><span class="mi">3</span><span class="p">])</span> <span class="p">{</span> <span class="mf">0.0000000</span><span class="p">,</span> <span class="mf">0.0000000</span><span class="p">,</span> <span class="mf">0.0000000</span> <span class="p">}</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">16</span><span class="p">]</span> <span class="n">Double_t</span> <span class="n">p</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mf">0.25</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="mf">0.75</span><span class="p">}</span>
<span class="p">(</span><span class="n">Double_t</span> <span class="p">[</span><span class="mi">3</span><span class="p">])</span> <span class="p">{</span> <span class="mf">0.25000000</span><span class="p">,</span> <span class="mf">0.50000000</span><span class="p">,</span> <span class="mf">0.75000000</span> <span class="p">}</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">17</span><span class="p">]</span> <span class="n">h2</span><span class="o">-></span><span class="n">GetQuantiles</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">q</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>
<span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="mi">3</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">18</span><span class="p">]</span> <span class="k">auto</span> <span class="n">iqr</span> <span class="o">=</span> <span class="n">q</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">-</span> <span class="n">q</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span> <span class="mf">1.4000000</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">19</span><span class="p">]</span> <span class="k">auto</span> <span class="n">range_min</span> <span class="o">=</span> <span class="n">q</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="mf">1.5</span> <span class="o">*</span> <span class="n">iqr</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span> <span class="mf">22.000000</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">20</span><span class="p">]</span> <span class="k">auto</span> <span class="n">range_max</span> <span class="o">=</span> <span class="n">q</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="mf">1.5</span> <span class="o">*</span> <span class="n">iqr</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span> <span class="mf">27.600000</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Now, we’re ready to put the values into histogram:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">21</span><span class="p">]</span> <span class="k">auto</span> <span class="n">hist</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">TH2I</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">range_max</span> <span class="o">-</span> <span class="n">range_min</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">range_min</span><span class="p">,</span> <span class="n">range_max</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">22</span><span class="p">]</span> <span class="n">hist</span><span class="o">-></span><span class="n">SetBarWidth</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="n">root</span> <span class="p">[</span><span class="mi">23</span><span class="p">]</span> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">v</span><span class="o">:</span> <span class="n">values</span><span class="p">)</span> <span class="p">{</span> <span class="n">hist</span><span class="o">-></span><span class="n">Fill</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> <span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>And draw the boxplot (<code>candle</code> means to draw a boxplot, please see the full reference <a href="https://root.cern/doc/master/classTHistPainter.html#HP01">here</a>):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">24</span><span class="p">]</span> <span class="n">hist</span><span class="o">-></span><span class="n">Draw</span><span class="p">(</span><span class="s">"candle plc"</span><span class="p">)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You could see how the canvas has changed and has a boxplot now
(the graph is deleted, however you could use <code>same</code> option to leave
the old graphics in place). Let’s save it to SVG:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="n">root</span> <span class="p">[</span><span class="mi">25</span><span class="p">]</span> <span class="n">c</span><span class="o">-></span><span class="n">Print</span><span class="p">(</span><span class="s">"boxplot.svg"</span><span class="p">)</span>
<span class="n">Info</span> <span class="n">in</span> <span class="o"><</span><span class="n">TCanvas</span><span class="o">::</span><span class="n">Print</span><span class="o">>:</span> <span class="n">SVG</span> <span class="n">file</span> <span class="n">boxplot</span><span class="p">.</span><span class="n">svg</span> <span class="n">has</span> <span class="n">been</span> <span class="n">created</span></code></pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhytOgGkiVQwq7vLD2xd_Azfs6t5cRTfU7tCl-VOixQSXOQ2mr2hS63RUfMTET4uL8EsUv693KqMnMgYZKKVxR-k3rMFhZ9pVuY2XSohhGg78Ln8noTyUSwchCMwRsMNBsvCH96CpBv4fE/s567/boxplot.png" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="600" data-original-height="387" data-original-width="567" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhytOgGkiVQwq7vLD2xd_Azfs6t5cRTfU7tCl-VOixQSXOQ2mr2hS63RUfMTET4uL8EsUv693KqMnMgYZKKVxR-k3rMFhZ9pVuY2XSohhGg78Ln8noTyUSwchCMwRsMNBsvCH96CpBv4fE/s600/boxplot.png"/></a></div>
</div>
</div>
<div class="paragraph">
<p>And that’s all! Surely, it’s only basics, and you could find much more documentation on
ROOT’s website. The last item I’d like to show you how to use ROOT
in the C++ program. It won’t require much work though.</p>
</div>
<div class="paragraph">
<p>First, you need to add the ROOT’s headers:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="cp">#include "TApplication.h"
#include "TCanvas.h"
#include "TColor.h"
#include "TGraph.h"
#include "TH1.h"
#include "TH2.h"
#include "TMultiGraph.h"
#include "TROOT.h"
#include "TStyle.h"</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Second, declare the entry point (start the application and run ROOT’s code):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
<span class="n">TApplication</span> <span class="n">app</span><span class="p">(</span><span class="s">"ROOT Application"</span><span class="p">,</span> <span class="o">&</span><span class="n">argc</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>
<span class="n">ping_stats</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="n">Run</span><span class="p">();</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Finally, just compile it:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>g++ ping_stats.C `root-config --cflags --libs`</pre>
</div>
</div>
<div class="paragraph">
<p>I hope that would give you some information to start working with ROOT. It’s
definitely worth looking into, and I hope it will help you in your research
and data analysis work. Happy hacking!</p>
</div>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-55038676002256696872021-03-07T21:36:00.002-07:002021-03-07T21:44:14.484-07:00Multicasting chat with encryption (ECDH P-256 + AES-256-CBC)<div id="content">
<div class="paragraph">
<p>Few years ago I published simple Python
<a href="https://nuald.blogspot.com/2008/06/static-code-analysis-tools-with.html">sample code</a>
for the multicasting chat, and this article is a long overdue follow-up
that takes that sample to a completely new level.</p>
</div>
<div class="paragraph">
<p>Current IT industry is unimaginable without cybersecurity features, and trends
like "zero-trust" rely on complicated crypto algorithms which could be quite
overwhelming for beginners. The example C++ code I’ve published could help you
to get the ideas and code snippets for your own projects. The full source code is
available on
<a href="https://gitlab.com/nuald/nuald-blogspot-com/-/blob/master/site/adoc/chat.cc">Gitlab</a>.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">DISCLAIMER</dt>
<dd>
<p>The code is provided as is, and has issues (including code duplication)
as it mostly serves as an education project, not the production-ready code that could
benefit from the proper class hierarchies, edge cases handling and better code coverage.</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>First, let’s review the main phases of the application:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Parse command line options (<code>-v</code> - verbose output, <code>-e</code> - apply encryption)</p>
</li>
<li>
<p>Generate EC (elliptic curve, NIST P-256 in the code) keypair. They usually call those keys <strong>ephemeral</strong>
in a context of using the keypair for the current session only.</p>
</li>
<li>
<p>Send the public key to other peers for ECDH (Elliptic-curve Diffie–Hellman) key agreement.
During that process they use their own private key and a third party public key to
generate a shared secret. That secret could be used to create a key that is used
for payload encryption/decryption between the host and the third party.</p>
</li>
<li>
<p>Usually you can’t use a shared secret as is, but rather have to derive a key from it.
The code uses SHA-256 hash, but it’s recommended to use KDF
(Key Derivation Function) like PBKDF2.</p>
</li>
<li>
<p>Send the chat messages encrypted with AES-256-CBC using the key generated above. Please
note that this cipher requires IV (Initialization Vector) that is included into the sent
payload. It’s not always necessary though (not all ciphers require it), plus some KDF
procedures generate (key, IV) pairs that could be used together.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Second, let me describe the communication protocol, or rather two types of messages a peer sends:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>REGISTER</strong>: <code>R.BASE64(<name>).BASE64(<public_key>)</code>. The purpose of the message is
informing other peers about the public key that is used in ECDH key agreement (step 3 above).
The message is sent in two use-cases:</p>
<div class="ulist">
<ul>
<li>
<p>Starting the chat. That also serves as a new peer notification to other peers in the local network.</p>
</li>
<li>
<p>Upon getting notification from other peers (i.e. if a <strong>REGISTER</strong> message with the
new <name> has been received). As a new peer doesn’t know yet the public keys
of other peers, we need to resend the public key to that peer. Generally speaking, that should be
the unicast message, but for the brevity we reuse the same multicast protocol.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>MESSAGE</strong>: <code>M.BASE64(<name>).BASE64(<IV>).BASE64(ENCRYPT(<message>, <key1>))…​BASE64(ENCRYPT(<message>, <keyN>))</code>.
That type is used to actually send the chat messages. As each peer has its
own shared secret, we need to use all secrets to encrypt the chat message,
thus all those <em>key<sub>i</sub></em> encrypted payloads.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Compilation, usage and test output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash"><span class="nv">$ </span>clang++ chat.cc <span class="nt">-lcrypto</span> <span class="nt">-pthread</span> <span class="nt">-o</span> chat
<span class="nv">$ </span>./chat <span class="nt">-ev</span>
Enter your name: alex
Encryption cipher: AES-256-CBC
Private key information:
Private-Key: <span class="o">(</span>256 bit<span class="o">)</span>
ASN1 OID: prime256v1
NIST CURVE: P-256
Public key information:
Public-Key: <span class="o">(</span>256 bit<span class="o">)</span>
ASN1 OID: prime256v1
NIST CURVE: P-256
Start listening...
Raw public key:
30 59 30 13 06 07 2a 86 48 ce 3d 02 01 06 08 2a
86 48 ce 3d 03 01 07 03 42 00 04 d1 f8 4e 2b 91
97 b3 96 35 bb 02 dc 6d 26 a0 63 09 e2 b1 00 da
5a 0a 80 27 7a 24 f8 10 d6 99 5a a0 <span class="nb">cd </span>6e 66 6f
3d 95 d0 ea 17 4c 9a da 8d 50 be ad 8f b9 5b 89
7c a3 27 13 60 fb e0 35 62 e2 a2
-> R.YWxleA<span class="o">==</span>.MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0fhOK5GXs5Y1uwLcbSagYwnisQDaWgqAJ3ok+BDWmVqgzW5mbz2V0OoXTJrajVC+rY+5W4l8oycTYPvgNWLiog<span class="o">==</span>
<- REGISTER alex KEY: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0fhOK5GXs5Y1uwLcbSagYwnisQDaWgqAJ3ok+BDWmVqgzW5mbz2V0OoXTJrajVC+rY+5W4l8oycTYPvgNWLiog<span class="o">==</span>
<- Raw public key:
30 59 30 13 06 07 2a 86 48 ce 3d 02 01 06 08 2a
86 48 ce 3d 03 01 07 03 42 00 04 d1 f8 4e 2b 91
97 b3 96 35 bb 02 dc 6d 26 a0 63 09 e2 b1 00 da
5a 0a 80 27 7a 24 f8 10 d6 99 5a a0 <span class="nb">cd </span>6e 66 6f
3d 95 d0 ea 17 4c 9a da 8d 50 be ad 8f b9 5b 89
7c a3 27 13 60 fb e0 35 62 e2 a2
<span class="nb">test </span>message
-> M.YWxleA<span class="o">==</span>.23ifu2ciV+W6XH/pxxGfEg<span class="o">==</span>.np2rmVf5Cddngs1JBFVTqA<span class="o">==</span><span class="nb">.</span>
<- MESSAGE alex IV: 23ifu2ciV+W6XH/pxxGfEg<span class="o">==</span>
<- PAYLOAD: <span class="nv">np2rmVf5Cddngs1JBFVTqA</span><span class="o">==</span>
alex: <span class="nb">test </span>message</code></pre>
</div>
</div>
<div class="paragraph">
<p>You could see <strong>REGISTER</strong> and <strong>MESSAGE</strong> messages above (and a successful attempt to decode <strong>PAYLOAD</strong>).
<code>tcpdump</code> (started earlier) shows that the traffic is encrypted indeed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash"><span class="nv">$ </span><span class="nb">sudo </span>tcpdump <span class="nt">-A</span> <span class="s1">'udp port 6543'</span>
tcpdump: verbose output suppressed, use <span class="nt">-v</span><span class="o">[</span>v]... <span class="k">for </span>full protocol decode
listening on wlp2s0, link-type EN10MB <span class="o">(</span>Ethernet<span class="o">)</span>, snapshot length 262144 bytes
19:45:54.431179 IP amber.59578 <span class="o">></span> 224.0.0.100.lds-distrib: UDP, length 135
E...m.@...h........d.......KR.YWxleA<span class="o">==</span>.MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0fhOK5GXs5Y1uwLcbSagYwnisQDaWgqAJ3ok+BDWmVqgzW5mbz2V0OoXTJrajVC+rY+5W4l8oycTYPvgNWLiog<span class="o">==</span>
19:45:56.636165 IP amber.59578 <span class="o">></span> 224.0.0.100.lds-distrib: UDP, length 61
E..Yn.@...g3.......d.....E..M.YWxleA<span class="o">==</span>.23ifu2ciV+W6XH/pxxGfEg<span class="o">==</span>.np2rmVf5Cddngs1JBFVTqA<span class="o">==</span>.</code></pre>
</div>
</div>
<div class="paragraph">
<p>If few peers are involved, <strong>MESSAGE</strong> contains several payloads. From another peer (<strong>gary</strong>) side (sending):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash">message from gary
-> M.Z2FyeQ<span class="o">==</span>.NgQe5SgHCvZWYmwKhy0lYw<span class="o">==</span>.xwAArSbH8ASK1zfor9ekLQTdLoniv7k1YLnjgwE6lpc<span class="o">=</span>.tor5juICo7QKClU3Qapism4km80sxgkAx6zYQrdpmfw<span class="o">=</span>.</code></pre>
</div>
</div>
<div class="paragraph">
<p>And local peer (<strong>alex</strong>) side (receiving):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash"><- MESSAGE gary IV: <span class="nv">NgQe5SgHCvZWYmwKhy0lYw</span><span class="o">==</span>
<- PAYLOAD: <span class="nv">xwAArSbH8ASK1zfor9ekLQTdLoniv7k1YLnjgwE6lpc</span><span class="o">=</span>
gary: message from gary
<- PAYLOAD: <span class="nv">tor5juICo7QKClU3Qapism4km80sxgkAx6zYQrdpmfw</span><span class="o">=</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>As you could see above there are several payload decryption attempts (and one successful).</p>
</div>
<div class="paragraph">
<p>And now let’s explore the actual snippets. I used OpenSSL (libcrypto) and POSIX functions
(mostly, for socket programming). The code has been tested in GNU/Linux (other platforms
may require few tweaks). Multithreading is implemented using native C++ functionality (please
note that it requires C++11 and higher support).</p>
</div>
<div class="paragraph">
<p>Few notes about the code:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>_(title, fn)</code> is the series of functions
to help with the error handling, you may ignore them except the <code>fn</code> part.</p>
</li>
<li>
<p>A lot of API uses <code>std::string</code> for the convenience only. Please consider
those data types as containers for bytes. In production code you may want
to use <code>std::vector<uint8_t></code> or even OpenSSL’s <code>BIO</code> structures directly.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Base64 encoding/decoding functions (<code>base64</code> namespace):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">encode</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">BIO</span><span class="o">></span> <span class="n">b64</span><span class="p">(</span><span class="n">BIO_new</span><span class="p">(</span><span class="n">BIO_f_base64</span><span class="p">()),</span> <span class="n">BIO_free_all</span><span class="p">);</span>
<span class="n">BIO_set_flags</span><span class="p">(</span><span class="n">b64</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">BIO_FLAGS_BASE64_NO_NL</span><span class="p">);</span>
<span class="k">auto</span> <span class="n">bio</span> <span class="o">=</span> <span class="n">BIO_new</span><span class="p">(</span><span class="n">BIO_s_mem</span><span class="p">());</span>
<span class="n">BIO_push</span><span class="p">(</span><span class="n">b64</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">bio</span><span class="p">);</span>
<span class="n">BIO_write</span><span class="p">(</span><span class="n">b64</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">data</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">data</span><span class="p">.</span><span class="n">size</span><span class="p">());</span>
<span class="n">BIO_flush</span><span class="p">(</span><span class="n">b64</span><span class="p">.</span><span class="n">get</span><span class="p">());</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">;</span>
<span class="k">auto</span> <span class="n">size</span> <span class="o">=</span> <span class="n">BIO_get_mem_data</span><span class="p">(</span><span class="n">bio</span><span class="p">,</span> <span class="o">&</span><span class="n">str</span><span class="p">);</span>
<span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">decode</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">BIO</span><span class="o">></span> <span class="n">b64</span><span class="p">(</span><span class="n">BIO_new</span><span class="p">(</span><span class="n">BIO_f_base64</span><span class="p">()),</span> <span class="n">BIO_free_all</span><span class="p">);</span>
<span class="n">BIO_set_flags</span><span class="p">(</span><span class="n">b64</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">BIO_FLAGS_BASE64_NO_NL</span><span class="p">);</span>
<span class="k">auto</span> <span class="n">bio</span> <span class="o">=</span> <span class="n">BIO_new_mem_buf</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">data</span><span class="p">.</span><span class="n">size</span><span class="p">());</span>
<span class="n">BIO_push</span><span class="p">(</span><span class="n">b64</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">bio</span><span class="p">);</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">BIO</span><span class="o">></span> <span class="n">bio_out</span><span class="p">(</span><span class="n">BIO_new</span><span class="p">(</span><span class="n">BIO_s_mem</span><span class="p">()),</span> <span class="n">BIO_free_all</span><span class="p">);</span>
<span class="kt">char</span> <span class="n">inbuf</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">inlen</span><span class="p">;</span>
<span class="k">while</span> <span class="p">((</span><span class="n">inlen</span> <span class="o">=</span> <span class="n">BIO_read</span><span class="p">(</span><span class="n">b64</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">inbuf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">inbuf</span><span class="p">)))</span> <span class="o">></span> <span class="mi">0</span> <span class="p">)</span> <span class="p">{</span>
<span class="n">BIO_write</span><span class="p">(</span><span class="n">bio_out</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">inbuf</span><span class="p">,</span> <span class="n">inlen</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">BIO_flush</span><span class="p">(</span><span class="n">bio_out</span><span class="p">.</span><span class="n">get</span><span class="p">());</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">;</span>
<span class="k">auto</span> <span class="n">size</span> <span class="o">=</span> <span class="n">BIO_get_mem_data</span><span class="p">(</span><span class="n">bio_out</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">str</span><span class="p">);</span>
<span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Please note that OpenSSL has <code>EVP_EncodeUpdate</code>/<code>EVP_DecodeBlock</code> helper functions,
but apparently they don’t use <code>BIO_FLAGS_BASE64_NO_NL</code>, therefore the encoded text
has PEM format (with new lines) that could be not compatible with some Base64 parsers.</p>
</div>
<div class="paragraph">
<p>Generate EC keypair (<code>crypto</code> namespace):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"> <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY</span><span class="o">></span> <span class="n">generate_pair</span><span class="p">(</span><span class="kt">int</span> <span class="n">nid</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY_CTX</span><span class="o">></span>
<span class="n">pctx</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_CTX_new_id"</span><span class="p">,</span>
<span class="n">EVP_PKEY_CTX_new_id</span><span class="p">(</span><span class="n">EVP_PKEY_EC</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">)),</span>
<span class="n">EVP_PKEY_CTX_free</span><span class="p">);</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_paramgen_init"</span><span class="p">,</span> <span class="n">EVP_PKEY_paramgen_init</span><span class="p">(</span><span class="n">pctx</span><span class="p">.</span><span class="n">get</span><span class="p">()));</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_CTX_set_ec_paramgen_curve_nid"</span><span class="p">,</span>
<span class="n">EVP_PKEY_CTX_set_ec_paramgen_curve_nid</span><span class="p">(</span><span class="n">pctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">nid</span><span class="p">));</span>
<span class="n">EVP_PKEY</span> <span class="o">*</span><span class="n">params_ptr</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_paramgen"</span><span class="p">,</span> <span class="n">EVP_PKEY_paramgen</span><span class="p">(</span><span class="n">pctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">params_ptr</span><span class="p">));</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY</span><span class="o">></span> <span class="n">params</span><span class="p">(</span><span class="n">params_ptr</span><span class="p">,</span> <span class="n">EVP_PKEY_free</span><span class="p">);</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">BIO</span><span class="o">></span> <span class="n">out</span><span class="p">(</span><span class="n">BIO_new_fp</span><span class="p">(</span><span class="n">stdout</span><span class="p">,</span> <span class="n">BIO_NOCLOSE</span><span class="p">),</span> <span class="n">BIO_free</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">verbose</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"Private key information:"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">EVP_PKEY_print_private</span><span class="p">(</span><span class="n">out</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">params</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="mi">3</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"Public key information:"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">EVP_PKEY_print_public</span><span class="p">(</span><span class="n">out</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">params</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="mi">3</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY_CTX</span><span class="o">></span>
<span class="n">kctx</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s">"CLT: EVP_PKEY_CTX_new"</span><span class="p">,</span>
<span class="n">EVP_PKEY_CTX_new</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="nb">nullptr</span><span class="p">)),</span>
<span class="n">EVP_PKEY_CTX_free</span><span class="p">);</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_keygen_init"</span><span class="p">,</span> <span class="n">EVP_PKEY_keygen_init</span><span class="p">(</span><span class="n">kctx</span><span class="p">.</span><span class="n">get</span><span class="p">()));</span>
<span class="n">EVP_PKEY</span> <span class="o">*</span><span class="n">pkey_ptr</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_keygen"</span><span class="p">,</span> <span class="n">EVP_PKEY_keygen</span><span class="p">(</span><span class="n">kctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">pkey_ptr</span><span class="p">));</span>
<span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY</span><span class="o">></span><span class="p">(</span><span class="n">pkey_ptr</span><span class="p">,</span> <span class="n">EVP_PKEY_free</span><span class="p">);</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The input parameter is <code>NID_X9_62_prime256v1</code> (that is NIST P-256 curve).
Output <code>EVP_PKEY</code> structure contains both public and private keys.</p>
</div>
<div class="paragraph">
<p>Extract public key from <code>EVP_PKEY</code> structure (<code>crypto</code> namespace):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">extract_public_key</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY</span><span class="o">></span> <span class="o">&</span><span class="n">pkey</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">BIO</span><span class="o">></span> <span class="n">bio</span><span class="p">(</span><span class="n">BIO_new</span><span class="p">(</span><span class="n">BIO_s_mem</span><span class="p">()),</span> <span class="n">BIO_free_all</span><span class="p">);</span>
<span class="n">_</span><span class="p">(</span><span class="s">"i2d_PUBKEY_bio"</span><span class="p">,</span> <span class="n">i2d_PUBKEY_bio</span><span class="p">(</span><span class="n">bio</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">pkey</span><span class="p">.</span><span class="n">get</span><span class="p">()));</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">;</span>
<span class="k">auto</span> <span class="n">size</span> <span class="o">=</span> <span class="n">BIO_get_mem_data</span><span class="p">(</span><span class="n">bio</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">str</span><span class="p">);</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">public_key</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">verbose</span><span class="p">)</span> <span class="p">{</span>
<span class="n">string</span><span class="o">::</span><span class="n">dump</span><span class="p">(</span><span class="s">"Raw public key:"</span><span class="p">,</span> <span class="n">public_key</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">public_key</span><span class="p">;</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>That public key is being sent to other peers. While it’s not required to escape
the binary data for the UDP datagrams, we use Base64 encoding on top of them
for the debugging purpose (e.g. the verbose output to the console).</p>
</div>
<div class="paragraph">
<p>Derive shared key (<code>crypto</code> namespace):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">derive_key</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY</span><span class="o">></span> <span class="n">pkey</span><span class="p">,</span>
<span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&</span><span class="n">public_key</span><span class="p">)</span> <span class="p">{</span>
<span class="k">typedef</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">></span> <span class="n">uc_vector</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">BIO</span><span class="o">></span> <span class="n">decoded</span><span class="p">(</span><span class="n">BIO_new</span><span class="p">(</span><span class="n">BIO_s_mem</span><span class="p">()),</span> <span class="n">BIO_free_all</span><span class="p">);</span>
<span class="n">BIO_write</span><span class="p">(</span><span class="n">decoded</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">public_key</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">public_key</span><span class="p">.</span><span class="n">size</span><span class="p">());</span>
<span class="n">EVP_PKEY</span> <span class="o">*</span><span class="n">peerkey_ptr</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
<span class="n">_</span><span class="p">(</span><span class="s">"d2i_PUBKEY_bio"</span><span class="p">,</span> <span class="n">d2i_PUBKEY_bio</span><span class="p">(</span><span class="n">decoded</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">peerkey_ptr</span><span class="p">));</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY</span><span class="o">></span> <span class="n">peerkey</span><span class="p">(</span><span class="n">peerkey_ptr</span><span class="p">,</span> <span class="n">EVP_PKEY_free</span><span class="p">);</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_PKEY_CTX</span><span class="o">></span>
<span class="n">ctx</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s">"SRV: EVP_PKEY_CTX_new"</span><span class="p">,</span>
<span class="n">EVP_PKEY_CTX_new</span><span class="p">(</span><span class="n">pkey</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="nb">nullptr</span><span class="p">)),</span>
<span class="n">EVP_PKEY_CTX_free</span><span class="p">);</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_derive_init"</span><span class="p">,</span> <span class="n">EVP_PKEY_derive_init</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">()));</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_derive_set_peer"</span><span class="p">,</span>
<span class="n">EVP_PKEY_derive_set_peer</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">peerkey</span><span class="p">.</span><span class="n">get</span><span class="p">()));</span>
<span class="kt">size_t</span> <span class="n">secret_len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_derive length"</span><span class="p">,</span>
<span class="n">EVP_PKEY_derive</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="nb">nullptr</span><span class="p">,</span> <span class="o">&</span><span class="n">secret_len</span><span class="p">));</span>
<span class="n">uc_vector</span> <span class="n">secret</span><span class="p">(</span><span class="n">secret_len</span><span class="p">);</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_PKEY_derive"</span><span class="p">,</span>
<span class="n">EVP_PKEY_derive</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">secret</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&</span><span class="n">secret_len</span><span class="p">));</span>
<span class="n">uc_vector</span> <span class="n">hash</span><span class="p">(</span><span class="n">SHA256_DIGEST_LENGTH</span><span class="p">);</span>
<span class="n">SHA256</span><span class="p">(</span><span class="o">&</span><span class="n">secret</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">secret_len</span><span class="p">,</span> <span class="o">&</span><span class="n">hash</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">hash</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">hash</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>pkey</code> is the host’s keypair, and <code>public_key</code> is the public key of the third party peer.
The derived secret is hashed using <code>SHA256</code> helper function and the resulting hash
is used as a key for symmetric encryption/decryption.</p>
</div>
<div class="paragraph">
<p>Symmetric encryption/decryption (<code>crypto</code> namespace):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cpp"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">encrypt</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">plaintext</span><span class="p">,</span>
<span class="k">const</span> <span class="n">EVP_CIPHER</span> <span class="o">*</span><span class="n">cipher</span><span class="p">,</span>
<span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">key</span><span class="p">,</span>
<span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">iv</span><span class="p">)</span> <span class="p">{</span>
<span class="k">typedef</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">></span> <span class="n">uc_vector</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_CIPHER_CTX</span><span class="o">></span> <span class="n">ctx</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s">"EVP_CIPHER_CTX_new"</span><span class="p">,</span>
<span class="n">EVP_CIPHER_CTX_new</span><span class="p">()),</span>
<span class="n">EVP_CIPHER_CTX_free</span><span class="p">);</span>
<span class="n">uc_vector</span> <span class="n">uc_key</span><span class="p">(</span><span class="n">key</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">key</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="n">uc_vector</span> <span class="n">uc_iv</span><span class="p">(</span><span class="n">iv</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">iv</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_EncryptInit_ex"</span><span class="p">,</span>
<span class="n">EVP_EncryptInit_ex</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">cipher</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span>
<span class="o">&</span><span class="n">uc_key</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&</span><span class="n">uc_iv</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="n">plaintext</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">+</span> <span class="n">EVP_CIPHER_block_size</span><span class="p">(</span><span class="n">cipher</span><span class="p">);</span>
<span class="n">uc_vector</span> <span class="n">ciphertext</span><span class="p">(</span><span class="n">len</span><span class="p">);</span>
<span class="n">uc_vector</span> <span class="n">uc_plaintext</span><span class="p">(</span><span class="n">plaintext</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">plaintext</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_EncryptUpdate"</span><span class="p">,</span>
<span class="n">EVP_EncryptUpdate</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span>
<span class="o">&</span><span class="n">ciphertext</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&</span><span class="n">len</span><span class="p">,</span>
<span class="o">&</span><span class="n">uc_plaintext</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">uc_plaintext</span><span class="p">.</span><span class="n">size</span><span class="p">()));</span>
<span class="kt">int</span> <span class="n">tmplen</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_EncryptFinal_ex"</span><span class="p">,</span>
<span class="n">EVP_EncryptFinal_ex</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">ciphertext</span><span class="p">[</span><span class="n">len</span><span class="p">],</span> <span class="o">&</span><span class="n">tmplen</span><span class="p">));</span>
<span class="n">ciphertext</span><span class="p">.</span><span class="n">resize</span><span class="p">(</span><span class="n">len</span> <span class="o">+</span> <span class="n">tmplen</span><span class="p">);</span>
<span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">ciphertext</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">ciphertext</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">decrypt</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">ciphertext</span><span class="p">,</span>
<span class="k">const</span> <span class="n">EVP_CIPHER</span> <span class="o">*</span><span class="n">cipher</span><span class="p">,</span>
<span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">key</span><span class="p">,</span>
<span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">iv</span><span class="p">)</span> <span class="p">{</span>
<span class="k">typedef</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">></span> <span class="n">uc_vector</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o"><</span><span class="n">EVP_CIPHER_CTX</span><span class="o">></span> <span class="n">ctx</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s">"EVP_CIPHER_CTX_new"</span><span class="p">,</span>
<span class="n">EVP_CIPHER_CTX_new</span><span class="p">()),</span>
<span class="n">EVP_CIPHER_CTX_free</span><span class="p">);</span>
<span class="n">uc_vector</span> <span class="n">uc_key</span><span class="p">(</span><span class="n">key</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">key</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="n">uc_vector</span> <span class="n">uc_iv</span><span class="p">(</span><span class="n">iv</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">iv</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_DecryptInit_ex"</span><span class="p">,</span>
<span class="n">EVP_DecryptInit_ex</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="n">cipher</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span>
<span class="o">&</span><span class="n">uc_key</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&</span><span class="n">uc_iv</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
<span class="n">uc_vector</span> <span class="n">uc_ciphertext</span><span class="p">(</span><span class="n">ciphertext</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">ciphertext</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="n">ciphertext</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">+</span> <span class="n">EVP_CIPHER_block_size</span><span class="p">(</span><span class="n">cipher</span><span class="p">);</span>
<span class="n">uc_vector</span> <span class="n">plaintext</span><span class="p">(</span><span class="n">len</span><span class="p">);</span>
<span class="n">_</span><span class="p">(</span><span class="s">"EVP_DecryptUpdate"</span><span class="p">,</span>
<span class="n">EVP_DecryptUpdate</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">plaintext</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&</span><span class="n">len</span><span class="p">,</span>
<span class="o">&</span><span class="n">uc_ciphertext</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">uc_ciphertext</span><span class="p">.</span><span class="n">size</span><span class="p">()));</span>
<span class="kt">int</span> <span class="n">tmplen</span><span class="p">;</span>
<span class="c1">// we expect failures as we try to decrypt using different keys</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">EVP_DecryptFinal_ex</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span> <span class="o">&</span><span class="n">plaintext</span><span class="p">[</span><span class="n">len</span><span class="p">],</span> <span class="o">&</span><span class="n">tmplen</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">plaintext</span><span class="p">.</span><span class="n">resize</span><span class="p">(</span><span class="n">len</span> <span class="o">+</span> <span class="n">tmplen</span><span class="p">);</span>
<span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">plaintext</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">plaintext</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>cipher</code> is a result of <code>EVP_aes_256_cbc()</code> call (AES-256-CBC). <code>key</code> length
could be retrieved using <code>EVP_CIPHER_key_length(cipher)</code>,
<code>iv</code> length - using <code>EVP_CIPHER_iv_length(cipher)</code> call.</p>
</div>
<div class="paragraph">
<p>Multicast socket programming is straightforward, you could review the code
by yourself. Let me just add few notes here:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Please ensure to use a proper multicast address
(i.e. ffx2::/16 for IPv6 and 224.0.0.0/24 for IPv4 are used for the local subnetwork only),
the sample app uses <code>224.0.0.100</code>.</p>
</li>
<li>
<p><code>IP_ADD_MEMBERSHIP</code> is required to receive multicast datagrams (please review all <code>setsockopt</code> usages in the app).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>That concludes the review of the encryption-enabled networking application.
Surely, it’s rather a sandbox than the real production code, but it could be
a starting point to the next secure product of yours. Happy hacking!</p>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2021-03-07 21:42:03 -0700
</div>
</div>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge .cm {
color: #999988;
font-style: italic;
}
pre.rouge .cp {
color: #999999;
font-weight: bold;
}
pre.rouge .c1 {
color: #999988;
font-style: italic;
}
pre.rouge .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cpf {
color: #999988;
font-style: italic;
}
pre.rouge .err {
color: #a61717;
background-color: #e3d2d2;
}
pre.rouge .gd {
color: #000000;
background-color: #ffdddd;
}
pre.rouge .ge {
color: #000000;
font-style: italic;
}
pre.rouge .gr {
color: #aa0000;
}
pre.rouge .gh {
color: #999999;
}
pre.rouge .gi {
color: #000000;
background-color: #ddffdd;
}
pre.rouge .go {
color: #888888;
}
pre.rouge .gp {
color: #555555;
}
pre.rouge .gs {
font-weight: bold;
}
pre.rouge .gu {
color: #aaaaaa;
}
pre.rouge .gt {
color: #aa0000;
}
pre.rouge .kc {
color: #000000;
font-weight: bold;
}
pre.rouge .kd {
color: #000000;
font-weight: bold;
}
pre.rouge .kn {
color: #000000;
font-weight: bold;
}
pre.rouge .kp {
color: #000000;
font-weight: bold;
}
pre.rouge .kr {
color: #000000;
font-weight: bold;
}
pre.rouge .kt {
color: #445588;
font-weight: bold;
}
pre.rouge .k, pre.rouge .kv {
color: #000000;
font-weight: bold;
}
pre.rouge .mf {
color: #009999;
}
pre.rouge .mh {
color: #009999;
}
pre.rouge .il {
color: #009999;
}
pre.rouge .mi {
color: #009999;
}
pre.rouge .mo {
color: #009999;
}
pre.rouge .m, pre.rouge .mb, pre.rouge .mx {
color: #009999;
}
pre.rouge .sa {
color: #000000;
font-weight: bold;
}
pre.rouge .sb {
color: #d14;
}
pre.rouge .sc {
color: #d14;
}
pre.rouge .sd {
color: #d14;
}
pre.rouge .s2 {
color: #d14;
}
pre.rouge .se {
color: #d14;
}
pre.rouge .sh {
color: #d14;
}
pre.rouge .si {
color: #d14;
}
pre.rouge .sx {
color: #d14;
}
pre.rouge .sr {
color: #009926;
}
pre.rouge .s1 {
color: #d14;
}
pre.rouge .ss {
color: #990073;
}
pre.rouge .s, pre.rouge .dl {
color: #d14;
}
pre.rouge .na {
color: #008080;
}
pre.rouge .bp {
color: #999999;
}
pre.rouge .nb {
color: #0086B3;
}
pre.rouge .nc {
color: #445588;
font-weight: bold;
}
pre.rouge .no {
color: #008080;
}
pre.rouge .nd {
color: #3c5d5d;
font-weight: bold;
}
pre.rouge .ni {
color: #800080;
}
pre.rouge .ne {
color: #990000;
font-weight: bold;
}
pre.rouge .nf, pre.rouge .fm {
color: #990000;
font-weight: bold;
}
pre.rouge .nl {
color: #990000;
font-weight: bold;
}
pre.rouge .nn {
color: #555555;
}
pre.rouge .nt {
color: #000080;
}
pre.rouge .vc {
color: #008080;
}
pre.rouge .vg {
color: #008080;
}
pre.rouge .vi {
color: #008080;
}
pre.rouge .nv, pre.rouge .vm {
color: #008080;
}
pre.rouge .ow {
color: #000000;
font-weight: bold;
}
pre.rouge .o {
color: #000000;
font-weight: bold;
}
pre.rouge .w {
color: #bbbbbb;
}
pre.rouge {
background-color: #f8f8f8;
}
</style>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-82067915447257719662020-12-07T22:17:00.002-07:002020-12-07T22:17:11.137-07:00Embedded Languages Footprint<div class="paragraph">
<p>One of my personal project has a requirement to have scripting support, and
while the research of the options is still in progress, I’d like to share
my preliminary results. The initial requirements for the scripting language
were having the smallest footprint, but good functionality. That has given a
start to the
<a href="https://gitlab.com/nuald-grp/embedded-langs-footprint">Embedded Languages Footprint</a>
project. The requirements are quite specific though (please see below),
hence the most popular languages like Lua and Python are not considered
suitable, but were included for the reference. For now, the most promising
languages are Chibi-Scheme and Wasm3, but it’s still on-going research and
final choice will be made later.</p>
</div>
<div class="paragraph">
<p>The major requirements:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>C/C++ only (to ensure the best portability);</p>
</li>
<li>
<p>No extra compilation dependencies (to build with the bare devkits);</p>
</li>
<li>
<p>Actively maintained (to compile without its source code changes);</p>
</li>
<li>
<p>Strong typing (to reduce the number of possible errors);</p>
</li>
<li>
<p>Permissive license for both open source and commercial projects;</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Additional requirement is ability to transform source code:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Bytecode is acceptable if its format is open and standardized;</p>
</li>
<li>
<p>One transformation is clean up the code from debug and unused functionality;</p>
</li>
<li>
<p>Another transformation is minifying private symbols;</p>
</li>
<li>
<p>Another optional transformation is removing new lines to reduce the final code size a little.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The original list of all possible languages is provided by another project:
<a href="https://github.com/dbohdan/embedded-scripting-languages">Embedded scripting languages</a>.
The example scripting code is the declaration
of the function that concatenates the fixed string ("Hello, ") with the output
of the external function ("read" that should return "world").
Resident set size (RSS) is measured during execution of "read" function.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">Platform</dt>
<dd>
<p>Linux amber 5.9.8-arch1-1 #1 SMP PREEMPT Tue, 10 Nov 2020 22:44:11 +0000 x86_64 GNU/Linux</p>
</dd>
</dl>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Language</th>
<th class="tableblock halign-left valign-top">ELF Size (KiB)</th>
<th class="tableblock halign-left valign-top">RSS (KiB)</th>
<th class="tableblock halign-left valign-top">Example Code</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">TinyScheme</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">84</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">3212</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>(define fn(lambda () (string-append "Hello, " (read))))</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Wasm3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">159</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">5904</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">N/A <sup>[1]</sup></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">MicroPython</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">186</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1976</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>import builtins; fn = lambda: 'Hello, ' + builtins.read()</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Lua</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">259</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1832</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>function fn() return "Hello, " .. read() end</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Chibi-Scheme</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">259</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">3504</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>(define fn(lambda () (string-append "Hello, " (read))))</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Squirrel</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">270</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1812</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>function fn() { return "Hello, " + read(); }</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ArkScript</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">443</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1828</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>(let fn(fun() (+ "Hello, " (read))))</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Gravity</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">516</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">3812</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>extern var read; func fn() { return "Hello, " + read(); }</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Janet</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">561</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">4300</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>(defn fn[] (string "Hello, " (read)))</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ChaiScript</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1342</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">5124</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>def fn() { return "Hello, " + read(); }</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">AngelScript</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1878</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">4908</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>string fn() { return 'Hello, ' + read(); }</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Python</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">3564</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">9460</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>import usermod; fn = lambda: 'Hello, ' + usermod.read()</code></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p><sup>[1]</sup> Wasm3 runs WebAssembly, therefore there is no reason to show the exact input
as it would be the binary content.
However, please see below the source code example that’s compiled into that binary.</p>
</div>
<div class="listingblock">
<div class="title">Rust source code (it uses <code>_m3</code>-wrappers to exchange blobs with host)</div>
<div class="content">
<pre class="highlight"><code class="language-rust" data-lang="rust">#[no_mangle]
pub fn r#fn() -> i32 {
let read_str = load_m3(unsafe { read() });
return store_m3(&format!("Hello, {}", read_str));
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>All the details are provided on the project page, however I’d like to share
my personal opinion regarding few languages:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>TinyScheme supports only subset of R5RS, used in Gimp. Chibi-Scheme supports
full and newer R7RS, therefore looks much more promising.</p>
</li>
<li>
<p>ChaiScript is quite interesting, easy to embed and supported by major companies.
However, it’s quite slow to compile and has a large footprint.</p>
</li>
<li>
<p>Gravity could be interested to people who like Swift as it has the similar syntax.
It’s definitely interesting language, but it doesn’t have much documentation yet.</p>
</li>
<li>
<p>Squirrel is quite small, but I can’t say it’s very easy to embed and debug.</p>
</li>
<li>
<p>MicroPython is not so friendly to the embedding as it assumes its own <code>main</code> function.
To properly embed it, one would need to write quite large Makefiles, and the lack of
documentation regarding embedding doesn’t help too.</p>
</li>
<li>
<p>Wasm3 is compact, but require external compiler to generate WASM-files. It could
be acceptable for some products though.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Chibi-Scheme has quite good library, plus additional modules
(including community-driven). I’d say that the only reason I’m still in doubt is
that it’s quite slow for my needs, and doesn’t have all features I’d like to have.
However, it’s definitely strong candidate for the embedding, and I’d
definitely recommend you to take a look to it for your future projects.</p>
</div>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-52365035036781145772020-10-13T14:36:00.006-06:002020-10-13T14:49:57.621-06:00AsciiDoctor and moving to GitLab<div class="paragraph">
<p>A lot of my current projects require writing technical documentation, and it
made me think quite carefully about the best practices here. I’ve found out
that there are few principles which make me the most effective.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Documentation as Code</strong> is the most important one. It has the advantages like:</p>
<div class="ulist">
<ul>
<li>
<p>The source of documentation could be version controlled with the
well-known and mature VCS like Git;</p>
</li>
<li>
<p>New changes could be pre-approved before submitting (e.g. as pull requests);</p>
</li>
<li>
<p>Proper VCS could give quite rich functionality (e.g. <code>git blame</code> to find the
author of the changes and the reason for them).</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Text Includes</strong> is another important principle as it allows to:</p>
<div class="ulist">
<ul>
<li>
<p>Re-use the documentation fragments (e.g. headers/footers);</p>
</li>
<li>
<p>Include live code snippets (copy-pasted snippets could easily get out of sync).</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Rich Formatting</strong> is a must-have for the user-friendly documentation:</p>
<div class="ulist">
<ul>
<li>
<p>Automatically generated ToC;</p>
</li>
<li>
<p>Offline syntax highlight of the source code. It allows to include
properly formatted code into the generated documents (PDF, HTML etc);</p>
</li>
<li>
<p>Tables with headers, alignments and merged cells.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<p>Not all markup systems could satisfy those principles
(like wide-spread Markdown), therefore I’ve decided to use Asciidoctor
(also know as AsciiDoc). I use it for few months already, and could confirm
that it is quite mature and reliable format to use.</p>
</div>
<div class="paragraph">
<p>However, I’ve discovered that not all systems properly support it.
In particular, I’d like to refer this GitHub issue:
<a href="https://github.com/github/markup/issues/1095">Asciidoctor: support include directives for other asciidoc files</a>.
It’s already 3 years old, got a significant number of follow-ups, but looks
like it’ll be never addressed (as some core developers left the team after
GitHub has been acquired by Microsoft).</p>
</div>
<div class="paragraph">
<p>And that makes me start the process of leaving GitHub,
and switching to GitLab - feature-wise the latter
is much more superioir to former (at least, the features I consider important
like proper Asciidoctor support). Surely, GitHub has polished UI and huge
community, but as a software developer I’m looking into other areas, not design
and social communications. The moving process is gradual and ad-hoc - I’m going
to move the project only I need to introduce some changes in it (and luckily,
GitLab has the nice feature of exporting from other systems including GitHub).</p>
</div>
<div class="paragraph">
<p>I’d highly recommend to check out AsciiDoc, its
<a href="https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/">quick reference</a>
gives the nice overview of its capabilities. And if you decide to use AsciiDoc
for your internal projects, I’d like to recommend GitLab too (and even if you
don’t use AsciiDoc, GitLab has the quite
<a href="https://about.gitlab.com/features/">extensive feature list</a> you may like).</p>
</div>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-82376212799730559892017-12-25T03:37:00.000-07:002017-12-25T05:04:12.427-07:00Management Tactics for the real world complex projects<style type='text/css'>/* Base16 Atelier Seaside Light - Theme */
/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside) */
/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */
/* Atelier-Seaside Comment */
.hljs-comment,
.hljs-quote {
color: #687d68;
}
/* Atelier-Seaside Red */
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #e6193c;
}
/* Atelier-Seaside Orange */
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #87711d;
}
/* Atelier-Seaside Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #29a329;
}
/* Atelier-Seaside Blue */
.hljs-title,
.hljs-section {
color: #3d62f5;
}
/* Atelier-Seaside Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #ad2bee;
}
.hljs {
display: block;
overflow-x: auto;
background: #f4fbf4;
color: #242924;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}.sign-container {
display: flex;
}
.sign-fixed {
width: 1.8em;
}
.sign-flex-item {
flex-grow: 1;
font-style: italic;
}</style>
<p>
A lot of management practices are used to optimize to current state of the projects and
get the situation under control,
but it does not always work as intended. Unfortunately, many people think that just applying the
required process does the trick, however, in many cases it does not. I've published the
presentation <a href="https://github.com/nuald/nuald-blogspot-com/raw/master/src/main/scalatex/MgmtTactics.pdf">Management Tactics</a> regarding that, and you can find
the corresponding notes with the detailed explanations below.
</p>
<a name='more'></a>
<p>
Before going further please let me notice that management practices do work,
however in many cases they're incorrectly used and that lead to frustrations
and the articles like
<a href="http://web.archive.org/web/20140923031500/http://gilesbowkett.blogspot.com.au/2014/09/why-scrum-should-basically-just-die-in.html">Why Scrum Should Basically Just Die In A Fire</a>
(it's a Web Archive copy, the original article has been removed). We live in the subjective reality
(please see <a href="https://www.newscientist.com/article/2128725-a-guide-to-why-your-world-is-a-hallucination/">A guide to why your world is a hallucination</a>),
but the objective reality gives us the challenges which we may overlook due to
differences between our perceptions. Let me try to point out few tactics
how to deal with those challenges and succeed in your projects.
</p>
<h1>
Points for Consideration
</h1>
<p>
The main points are described below, and the rest of the presentation is just
the examples of those points, including their reasons and possible solutions.
</p>
<ul>
<li>
There is No Silver Bullet
<p>
Please keep your expectation low - we're dealing with the highly volatile factor
(the human) and we can't plan/predict everything with the high accuracy.
If some method says that it can deal with all the problems, please be skeptical.
Good example - <a href="https://medium.com/quality-faster/the-zero-bug-policy-b0bd987be684">Zero-Bug Software Development</a>. While it has some valid points, and can
be applied for some projects, we have to take it with a grain of salt - bugs
are still there, just called differently.
</p></li>
<li>
Be Rational (e.g. legacy ≠ untouchable)
<p>
While we prefer to think about ourselves that we're rational, it's not always true.
Please try to answer a question - would you put all your fortune on a statement that
any new combination of properly shuffled deck of 52 cards has never been
seen in the history of universe? It's tough to say "yes", but actually
52! is a huge number, and the Solar System will disappear
faster than shuffling will produce the same combination even if we're doing it with the speed of
million combinations per second.
</p>
<p>
One of the most infamous irrationality is "if it ain't broke, don't fix it".
Environment is changing in many ways all the time,
and the things which were working yesterday, could be broken tomorrow. Y2K bug
costed <a href="https://www.theguardian.com/technology/2000/jan/05/y2k.guardiananalysispage">billions of dollars</a>,
Unix Millennium Bug (2038 year) may cost even more. Using outdated technologies
like Cobol creates a hiring crisis and money loss:
<a href="https://www.reuters.com/article/us-usa-banks-cobol/banks-scramble-to-fix-old-systems-as-it-cowboys-ride-into-sunset-idUSKBN17C0D8">Banks scramble to fix old systems as IT 'cowboys' ride into sunset</a>.
</p></li>
<li>
Do Risk Analysis
<p>
However, being on a "cutting edge" may have a cost too. For example, nowadays
front-end technologies move very fast, and it's very difficult to keep up the speed.
Moreover, new changes may introduce new bugs, and some of the new technologies
don't worth to be even used.
</p></li>
<li>
Take Interest in Psychology
<p>
We're working with people, not machines, and it gives us the challenges which
we may overlook. Few examples are described below in other sections.
</p></li>
<li>
Make <a href="https://en.wikipedia.org/wiki/Knowledge-based_decision_making">Knowledge-based Decisions</a>
<p>
Intuition may fail us, and it's better to make decisions based on the knowledge.
Surely, everybody knows the importance of prototyping and research,
however let me notice another factor - transparency. We couldn't know
anything, and sometimes it's useful to include teammates in the decision
process because they may have insights you could be unaware of. Interdisciplinary studies
gave us neural networks and deep learning technologies which affect
the whole IT industry now.
</p></li></ul>
<h1>
Everybody Lies
</h1>
<p>
While people prefer to think about themselves as honest, in reality it's not
always true, and sometimes we lie and are being even unaware of it. Even criminals
have self-justification and use a reasoning like thinking about themselves as noble and positive.
</p>
<h2>
Reasons
</h2>
<ul>
<li>
Personal/Financial Motives
<p>
Please note that not only your staff may have personal (including financial) motives,
but vendors and subcontractors too. Don't be a marionette of the marketing,
and please don't think that if everyone (big players, your friends, fortune 50 companies)
are using some technology, you have to use the same one. They might just be stuck
with it or have irrational reasoning like "if it ain't broke, don't fix it".
For example, <a href="https://danluu.com/anon-benchmark/">Larry Ellison allegedly tried to have a professor fired for benchmarking Oracle</a>, and while it doesn't mean that
Oracle or MS SQL servers are slow, you should consider your own benchmarking
before deciding which servers to use - it's quite possible that the
published benchmarks are just the marketing.
</p></li>
<li>
Laziness
<p>
Laziness is not always bad, especially if a developer automates the processes,
making it possible to be lazy later. We're not machines, and sometimes
we just don't have a mood to work hard today, could have some family/personal issues
thus couldn't work effectively. I think it's completely fine given
the tasks are not stalled and finished in time. Moreover, it became pretty much
common to reduce work hours to make staff more satisfied with job hence
increasing productivity: <a href="https://www.shrm.org/hr-today/news/hr-magazine/0217/pages/is-it-time-to-kill-the-40-hour-workweek.aspx">Is It Time to Kill the 40-Hour Workweek?</a>
However, there will always be people who want to abuse the system, and
just to be lazy. No process can absolutely help with it (without the price
of everybody suffering), and it's a responsibility of managers to take care of it.
</p></li>
<li>
Psychological Immune System
<p>
It's not an official term, and I'm referring to TED Talks here:
<a href="https://www.ted.com/talks/dan_gilbert_asks_why_are_we_happy">The surprising science of happiness</a>.
It's important to know that the role of unconscious could be much bigger
than we expect, and we have to be ready to deal with it. One of the most
infamous examples here - programmers' obsession with the technologies they use
within the long periods of time. At some point, they can't listen to any
rational reasons against the technology they like, because their unconscious
protects them from the possible stress that could occur after realizing that they invested
so much efforts into something flawed and not so suitable for their tasks.
</p></li>
<li>
<a href="https://rationalwiki.org/wiki/Dunning-Kruger_effect">Dunning-Kruger effect</a>
<p>
Another infamous characteristic of many programmers - they're oblivious to
any problems in their code, because don't have a competency to see them. Moreover,
they're fiercely protecting their code therefore making any critique
a personal matter to them.
</p></li></ul>
<h2>
Solutions
</h2>
<ul>
<li>
Common Vocabulary
<p>
Using clear terms may help with misunderstanding and reduces a risk that
developers use the worst definition of the term thus allowing them to be lazy
and blame their managers for being not clear enough. E.g., please avoid
using generic "concurrency" term in the design documents, but use the concrete
multiprocessing/multithreaded/clustering requirements.
</p>
<p>
Using common code style may help with Dunning-Kruger effect - enforcing
certain rules can make code much better, and while initially you may see
some resistance, eventually (due to Psychological Immune System), developers
will like it. Please note that strict enforcing works better (because
it pushes Psychological Immune System to work) comparing with giving the options
("I'd like you to do something, but it's not required"), and even more,
the latter may cause a prolonged conflict.
</p></li>
<li>
Task → Artefacts
<p>
The tasks should be always verified by the produced artefacts. If a task
doesn't generate anything, then it will be abused. If you ask, "please do
the research and make a decision regarding whether this functionality possible or not,"
there is a high probability that the researcher will spend weeks doing nothing and at
the end says, "It's not possible." However, if you ask, "please do
the research, provide me with the references you find and
make a decision whether this functionality possible or not," the possibility of abusing
could be much lower.
</p></li>
<li>
History of Changes
<p>
Tracking history of the task artefacts gives an ability to make the rational
decisions about the performance of the developer. Being lazy for only a week
could be acceptable, but being lazy for months could be a sign
that we need to let this developer go. Having history of changes may help a lot with
the decisions like that.
</p></li></ul>
<h1>
Shit Happens
</h1>
<p>
A lot of management methods do not work well with the deadlines and other
stress-related circumstances. Waterfall (and many agile) practices crumble upon it,
and consequences may be catastrophic.
</p>
<h2>
Reasons
</h2>
<ul>
<li>
Incorrect ETA
<p>
Having the accurate plans is almost impossible with the volatile factors like
humans, however the business still requires some estimations, and surely
it fails very often.
</p></li>
<li>
Workforce Issues
<p>
People may become ill, they have families with children, occasional vacations,
and may even want to sabotage their work if they have some grudges against
teammates or the company. All of those may lead to unintended delays and
other problems with the projects.
</p></li>
<li>
Uncontrollable Environment
<p>
OS glitches, viruses, hardware failures, 3rd library broken updates
(<a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/">How one developer just broke Node, Babel and thousands of projects in 11 lines of JavaScript</a>) may
have catastrophic consequences to the projects you're working on and you
have to be ready to deal with it.
</p></li></ul>
<h2>
Solutions
</h2>
<ul>
<li>
Thresholds
<p>
Having reasonable thresholds regarding certain metrics like latency, bandwidth,
code coverage etc. may help with keeping situation under control in the events
of stress-related circumstances. While having 100% code coverage is nice, during
a deadline it would be very difficult to write the fully covered code. At the
same time, not measuring any coverage could be harmful too, because some
critical code may not be tested and you won't know about it. Having some threshold
(like 75% coverage) could a reasonable compromise.
</p></li>
<li>
Proper Tests
<p>
All levels of testing are useful. You may have unit-tests passing, but
a smoke test verifying that the program could be started could fail. Integration tests
may take a while and during deadline you may have problems running it
each time the code changes, and having minimal smoke tests may help in this situation.
Continuous integration with the <b>isolated</b> environment (with source codes
not being up-to-dated, but cloned each time from a repository) could greatly
help too (e.g., for verification that all source codes are actually committed
into the source tree).
</p></li>
<li>
Collaboration
<p>
Please use the suitable tools for collaboration. For example, having
everything in tickets makes transferring the knowledge no mean feat, and forbidding
to use IM could make the communications flow very slow. On the other hand,
if it's allowed, it's tempting to discuss all work-related matters using IM,
and it's a responsibility of the manager to track it, and help people to understand
the proper usage of tools at their disposal.
</p></li>
<li>
Rules as Intended > Rules as Written
<p>
A lot of managers put the Agile processes before the people, enforcing
them to follow the procedures and waste their time for formalities, not
the actual work. However, the creators of Agile methods didn't want it,
and it's expressed in their <a href="http://agilemanifesto.org/">Agile Manifesto</a>:
<b>Individuals and interactions over processes and tools</b>. Sometimes we need
to bend the rules, because they do not fit the current situation and may cause
more harm than good.
</p></li></ul>
<h1>
Thresholds
</h1>
<p>
It's almost impossible to create the perfect software, and even if it's close to perfect
in one environment (with predefined OS/hardware/usage scenarios), it may fail in another
environment. Right environment and tools can help us getting better results (hence, use higher
thresholds), and it should always be considered before starting the project.
For example, when switching to slower but feature-rich technology, you
can do <a href="https://www.optimizely.com/optimization-glossary/ab-testing/">A/B Testing</a>
regarding what latency is acceptable for users. You don't
always need to use the best technologies (it has some cost in the most cases),
and using a little bit inferior but still acceptable technology
could make the development much cheaper.
</p>
<h1>
History of Changes
</h1>
<p>
Retrospective analysis of the project (knowing not only the current state,
but the history of events concluded in this state) can
increase the prediction accuracy and help with planning. For example, when you're
researching the features similar to the ones you have worked with before, you can use
the retrospective analysis with the help of VCS (i.e., how many days you've spent on similar
features based on number of days having commits involving those features;
and deviations can help with calculation of pessimistic/optimistic time boundaries).
</p>
<p>
Almost all types of activities produce the artefacts (directly or indirectly -
for example, we can just register the activities being done with audio/video capture).
Brainstorming (i.e., research) tasks can follow the scheme: the whiteboard hypotheses ->
photoshoots of the whiteboard (+ put in VCS) -> prototyping using programming or SQL
(+ put code in VCS) -> results (+ exported as text and saved in VCS) ->
back to the whiteboard or rollback to the previous hypothesis/prototypes/results.
</p>
<h1>
Managerial Tasks
</h1>
<p>
While it's widely believed that the task trackers can be used directly for
planning and reporting, it's not an easy task to keep the data there consistent and useful.
It's much better to consider the trackers as tools for convenience, and don't completely rely
on them for reporting.
</p>
<ul>
<li>
Don't trust the tracker, but only artefacts
<p>
If managers blindly believe the tracker, it leads to abusing it (for example,
if performance is measured by the number of closed tickets, the developers
and testers begin to open dozens of tickets even for minor issues).
</p></li>
<li>
Done for developer ≠ Done for manager
<p>
All people are different, and managers and developers have different understandings
of the requirements.
A developer may think that the feature is implemented, but the manager - not
(usually managers have better understanding of the business requirements and
can notice the use-cases which were overlooked by the developer). Also, if managers
verify the features implementation, they can see the progress closely thus
the probability of getting the situation out of control is getting lower.
</p></li>
<li>
Checklists could be artefacts by themselves
<p>
Developers and managers have their own checklists: developer's
is based on real artefacts (code and documentation), manager's is
based on the manager activities (like verifying that the business requirements
are taken care of). It allows us to find bottlenecks by analyzing the dynamics
of the changes, also time-based checklists can be re-verified later (for example,
if CI has stopped delivering correct results and the manager has been changing the checklist
based on those results, it has the high probability that the manager has
been just lazy and put the ticks without any verification). It’s not needed so often,
but can improve the discipline in the company (when managers know that
their work can be analyzed later).
</p></li>
<li>
Reports are based on artefacts and their dynamics
<p>
Task trackers do provide valuable information, but aggregated reports
from other sources like VCS commits could provide more accurate and useful
results. Please note that you may use task trackers indirectly (via
reading its meta information and databases), hence you may get better insights
(see the examples in the presentation).</p></li></ul>
<pre margin='0.5em'>Scalateχ \src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/MgmtTacticsScript.scalatex" target="_blank">MgmtTacticsScript.scalatex</a></pre>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com3tag:blogger.com,1999:blog-3721596730656231421.post-27107998700068655182017-11-29T04:11:00.000-07:002017-12-13T04:15:03.805-07:00A Sample of Lambda Architecture Project<style type='text/css'>/* Base16 Atelier Seaside Light - Theme */
/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside) */
/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */
/* Atelier-Seaside Comment */
.hljs-comment,
.hljs-quote {
color: #687d68;
}
/* Atelier-Seaside Red */
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #e6193c;
}
/* Atelier-Seaside Orange */
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #87711d;
}
/* Atelier-Seaside Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #29a329;
}
/* Atelier-Seaside Blue */
.hljs-title,
.hljs-section {
color: #3d62f5;
}
/* Atelier-Seaside Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #ad2bee;
}
.hljs {
display: block;
overflow-x: auto;
background: #f4fbf4;
color: #242924;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}.sign-container {
display: flex;
}
.sign-fixed {
width: 1.8em;
}
.sign-flex-item {
flex-grow: 1;
font-style: italic;
}</style>
<p>
I would like to present my pet project:
<a href="https://github.com/nuald/lambda-sample">A Sample of Lambda Architecture Project</a>,
the boilerplate for detecting IoT sensor anomalies using the Lambda architecture.
It's based on Akka/Scala cluster, and utilizes Cassandra database and Redis in-memory data store.
</p>
<a name='more'></a>
<p>
The project assumes two data processing layers:
the fast one (to ensure SLA requirements for latency)
and another one based on machine learning (to ensure low rate of false positives).
One may imagine it as a data flow graph from one origin (events) with two branches
(those processing layers), hence the Lambda architecture (λ symbol). Please note
that the classic Lambda architecture is slightly different, however the idea is
similar.
</p>
<p>
The details are available on the project page, plus I'm going to expand the project
with the additional functionality (using protobufs, real world clustering and
more benchmarks with the analysis actor in different programming languages). Let me just note
here the code examples to give better understanding how Lambda architecture works in
the project.
</p>
<p>
Speed layer is directly used by the analysis Akka actor (with the history of events saved
in Cassandra database). The layer uses the heuristics based on the standard deviation:
</p>
<pre><p style="margin: 0.5em;">\src: <a href="https://github.com/nuald/lambda-sample/tree/master/src/main/scala/analyzer/Analyzer.scala#L37-L60" target="_blank">Analyzer.scala</a></p><code class="scala hljs"><span class="hljs-comment">// ANCHOR: withHeuristic begin</span>
<span class="hljs-comment">/**
* Calculates the probability of the anomaly with the heuristics.
*
* @param value The analyzed value
* @param history The previous values
* @return The probability of the anomaly
*/</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">withHeuristic</span></span>(value: <span class="hljs-type">Double</span>, history: <span class="hljs-type">Iterable</span>[<span class="hljs-type">Double</span>]): <span class="hljs-type">Double</span> = {
<span class="hljs-keyword">val</span> size = history.size
<span class="hljs-keyword">val</span> avg = history.sum / size
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sqrDiff</span></span>(x: <span class="hljs-type">Double</span>) = (x - avg) * (x - avg)
<span class="hljs-keyword">val</span> stdDev = math.sqrt((<span class="hljs-number">0.0</span> /: history)(_ + sqrDiff(_)) / size)
<span class="hljs-keyword">val</span> valueDev = math.abs(value - avg)
<span class="hljs-keyword">val</span> anomaly = (valueDev - stdDev) / (<span class="hljs-number">2</span> * stdDev)
<span class="hljs-comment">// truncate the value to be in the [0, 1] interval</span>
anomaly.max(<span class="hljs-number">0</span>).min(<span class="hljs-number">1</span>)
}</code></pre>
<p>
Batch layer uses the Random Forest classification model (machine learning
algorithms are provided by <a href="https://haifengl.github.io/smile/">Smile</a> engine).
The corresponding actor runs the batch analysis on a schedule
and saves the model into Redis in-memory data store. The model is used later
by the analysis actor:
</p>
<pre><p style="margin: 0.5em;">\src: <a href="https://github.com/nuald/lambda-sample/tree/master/src/main/scala/analyzer/Analyzer.scala#L62-L78" target="_blank">Analyzer.scala</a></p><code class="scala hljs"><span class="hljs-comment">// ANCHOR: withTrainedModel begin</span>
<span class="hljs-comment">/**
* Calculates the probability of the anomaly with the trained model.
*
* @param value The analyzed value
* @param rf The trained model (Random Forest classification)
* @return The probability of the anomaly
*/</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">withTrainedModel</span></span>(value: <span class="hljs-type">Double</span>, rf: <span class="hljs-type">RandomForest</span>): <span class="hljs-type">Double</span> = {
<span class="hljs-keyword">val</span> probability = <span class="hljs-type">ML</span>.<span class="hljs-type">RandomForest</span>(rf).predict(<span class="hljs-type">Array</span>(value))
<span class="hljs-comment">// anomaly class has the index 1</span>
probability(<span class="hljs-number">1</span>)
}</code></pre>
<p>
Serving layer is represented by the analysis Akka actor. It uses the results from
both speed and batch layers with the weighted average:
</p>
<pre><p style="margin: 0.5em;">\src: <a href="https://github.com/nuald/lambda-sample/tree/master/src/main/scala/analyzer/Analyzer.scala#L122-L147" target="_blank">Analyzer.scala</a></p><code class="scala hljs"><span class="hljs-comment">// ANCHOR: analyze begin</span>
<span class="hljs-comment">/**
* Calculates the probability of the anomaly using both heuristics and trained model
*
* @param name Name of the IoT sensor
* @param entries Sensor history entries
* @param rf Optionally trained model
* @return Meta information with the results of the analysis
*/</span>
<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">analyze</span></span>(name: <span class="hljs-type">String</span>, entries: <span class="hljs-type">Iterable</span>[<span class="hljs-type">Entry</span>], rf: <span class="hljs-type">Option</span>[<span class="hljs-type">RandomForest</span>]) = {
<span class="hljs-keyword">val</span> values = entries.map(_.value)
<span class="hljs-keyword">val</span> value = values.head
<span class="hljs-keyword">val</span> approxAnomaly = <span class="hljs-type">Analyzer</span>.withHeuristic(value, values)
<span class="hljs-keyword">val</span> mlAnomalyOpt = rf.map(<span class="hljs-type">Analyzer</span>.withTrainedModel(value, _))
<span class="hljs-keyword">val</span> avgAnomaly = mlAnomalyOpt <span class="hljs-keyword">match</span> {
<span class="hljs-keyword">case</span> <span class="hljs-type">Some</span>(mlAnomaly) => (<span class="hljs-number">35.0</span> * approxAnomaly + <span class="hljs-number">65.0</span> * mlAnomaly) / <span class="hljs-number">100.0</span>
<span class="hljs-keyword">case</span> <span class="hljs-type">None</span> => approxAnomaly
}
<span class="hljs-keyword">val</span> ts = <span class="hljs-keyword">new</span> <span class="hljs-type">Date</span>(<span class="hljs-type">System</span>.currentTimeMillis)
<span class="hljs-type">SensorMeta</span>(name, ts, approxAnomaly, mlAnomalyOpt.getOrElse(<span class="hljs-number">-1</span>), avgAnomaly)
}</code></pre>
<p>
The actor can run within a cluster therefore providing the scalability and required
performance (please note that Cassandra and Redis replicas might be needed though). The
project describes a sample cluster configuration (with manual setup), but it surely
could be automated.</p>
<pre margin='0.5em'>Scalateχ \src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/LambdaSample.scalatex" target="_blank">LambdaSample.scalatex</a></pre>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-60114493634975211882017-10-04T19:22:00.000-06:002017-12-13T04:18:12.367-07:00Running arbitrary containers in LinuxKit<style type='text/css'>/* Base16 Atelier Seaside Light - Theme */
/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside) */
/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */
/* Atelier-Seaside Comment */
.hljs-comment,
.hljs-quote {
color: #687d68;
}
/* Atelier-Seaside Red */
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #e6193c;
}
/* Atelier-Seaside Orange */
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #87711d;
}
/* Atelier-Seaside Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #29a329;
}
/* Atelier-Seaside Blue */
.hljs-title,
.hljs-section {
color: #3d62f5;
}
/* Atelier-Seaside Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #ad2bee;
}
.hljs {
display: block;
overflow-x: auto;
background: #f4fbf4;
color: #242924;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}.sign-container {
display: flex;
}
.sign-fixed {
width: 1.8em;
}
.sign-flex-item {
flex-grow: 1;
font-style: italic;
}</style>
<p>
Debugging issues with <a href="https://github.com/linuxkit/linuxkit">LinuxKit</a>
images could be a serious challenge in some cases. Unfortunately, the documentation
is not full enough to cover all the caveats, and in this article I'm going to show
the general principle how to deal with arbitrary containers and run the commands
in them.
</p>
<a name='more'></a>
<p>
You have three main tools to use:
</p>
<ol>
<li>
Logs (<code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">/var/log/<container>.err.log</code> and <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">/var/log/<container>.out.log</code>) -
please examine these files first to see what's happening;</li>
<li>
CLI (<b>getty</b>) is the main tool at your disposal;</li>
<li>
Mounts (<code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">binds</code>) - the mount points to exchange the information between containers (<b>getty</b>
writes the file, and your container reads it).
</li></ol>
<p>
It's better to see something once than hear it, so please just look to
the Yaml configuration below. For this example,
I've added the <b>curl</b> image and used it from the shell:
</p>
<pre><code class="shell hljs">run curl https://www.google.com
</code></pre>
<p>
<i>
Please note that LinuxKit has <b>wget</b> available, so in most cases you won't
need <b>curl</b> for your experiments.
</i></p>
<p>
How it works:
</p>
<ul>
<li>
<b>curl</b> is not a service, but a usual program (however, to make it availble
to the <b>ctr</b> command we put the image into <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">services</code> section).
Because we need to exhange
the information using a file, we don't run <b>curl</b> as is, but provide
parameters to it from <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">/hostroot/var/cmd</code>.</li>
<li>
<code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">run</code> is an alias in <b>getty</b>: it deletes the container (otherwise it
won't be able to start the container later), writes the required parameters into the file,
and starts the container again.</li>
<li>
On startup the <b>curl</b> reads the up-to-dated parameters from the file, and uses
the required parameters for running (e.g., opens <i>https://www.google.com</i>).
</li></ul>
<pre><p style="margin: 0.5em;">\src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/linuxkit-curl.yml" target="_blank">linuxkit-curl.yml</a></p><code class="yml hljs"><span class="hljs-attr">kernel:</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">linuxkit/kernel:4.9.52</span>
<span class="hljs-attr"> cmdline:</span> <span class="hljs-string">"console=tty0 console=ttyS0 console=ttyAMA0 vga=791"</span>
<span class="hljs-attr">init:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit/init:7804129bd06218b72c298139a25698a748d253c6</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit/runc:a1b564248a0d0b118c11e61db9f84ecf41dd2d2a</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit/containerd:417f83f7b8dc1fa36acf90effe44f99c7397480a</span>
<span class="hljs-attr">onboot:</span>
<span class="hljs-attr"> - name:</span> <span class="hljs-string">dhcpcd</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">linuxkit/dhcpcd:d4408777ed6b6e6e562a5d4938fd09804324b33e</span>
<span class="hljs-attr"> command:</span> <span class="hljs-string">["/sbin/dhcpcd",</span> <span class="hljs-string">"--nobackground"</span><span class="hljs-string">,</span> <span class="hljs-string">"-f"</span><span class="hljs-string">,</span> <span class="hljs-string">"/dhcpcd.conf"</span><span class="hljs-string">,</span> <span class="hljs-string">"-1"</span><span class="hljs-string">]</span>
<span class="hljs-attr">services:</span>
<span class="hljs-attr"> - name:</span> <span class="hljs-string">getty</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">linuxkit/getty:bf6872ce0a9f3ab519b3e502cc41ba3958bda2a6</span>
<span class="hljs-attr"> env:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">INSECURE=true</span>
<span class="hljs-attr"> binds:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/etc/resolv.conf:/etc/resolv.conf</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/run:/run</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/tmp:/tmp</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/etc:/hostroot/etc</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/usr/bin/ctr:/usr/bin/ctr</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/usr/bin/runc:/usr/bin/runc</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/containers:/containers</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/var/log:/var/log</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/dev:/dev</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/sys:/sys</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/etc/profile.d/run.sh:/etc/profile.d/run.sh</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/var:/hostroot/var:rshared,rbind</span>
<span class="hljs-attr"> rootfsPropagation:</span> <span class="hljs-string">shared</span>
<span class="hljs-attr"> - name:</span> <span class="hljs-string">curl</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">byrnedo/alpine-curl</span>
<span class="hljs-attr"> command:</span> <span class="hljs-string">["sh",</span> <span class="hljs-string">"-c"</span><span class="hljs-string">,</span> <span class="hljs-string">"curl `cat /hostroot/var/cmd`"</span><span class="hljs-string">]</span>
<span class="hljs-attr"> binds:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/var:/hostroot/var</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/etc/resolv.conf:/etc/resolv.conf</span>
<span class="hljs-attr">files:</span>
<span class="hljs-attr"> - path:</span> <span class="hljs-string">etc/profile.d/run.sh</span>
<span class="hljs-attr"> contents:</span> <span class="hljs-string">|
run() {
ctr t delete $1
echo $2 > /hostroot/var/cmd
ctr t start $1
}
</span><span class="hljs-attr">trust:</span>
<span class="hljs-attr"> org:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit</span></code></pre>
<pre margin='0.5em'>Scalateχ \src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/LinuxKitCurl.scalatex" target="_blank">LinuxKitCurl.scalatex</a></pre>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-79883972766060855162017-10-02T01:34:00.000-06:002017-12-13T04:16:38.402-07:00Using LinuxKit on real hardware<style type='text/css'>/* Base16 Atelier Seaside Light - Theme */
/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside) */
/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */
/* Atelier-Seaside Comment */
.hljs-comment,
.hljs-quote {
color: #687d68;
}
/* Atelier-Seaside Red */
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #e6193c;
}
/* Atelier-Seaside Orange */
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #87711d;
}
/* Atelier-Seaside Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #29a329;
}
/* Atelier-Seaside Blue */
.hljs-title,
.hljs-section {
color: #3d62f5;
}
/* Atelier-Seaside Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #ad2bee;
}
.hljs {
display: block;
overflow-x: auto;
background: #f4fbf4;
color: #242924;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}.sign-container {
display: flex;
}
.sign-fixed {
width: 1.8em;
}
.sign-flex-item {
flex-grow: 1;
font-style: italic;
}</style>
<p>
<a href="https://github.com/linuxkit/linuxkit">LinuxKit</a> is a promising
toolkit for building secure, portable and lean operating systems for containers.
One of its advantages is having a predictable container set (in other words,
installed packages in the system), hence providing a homomorphic environment
for QA and benchmarking tests.
</p>
<p>
However, the project is fairly new, and users may have problems setting
and running it up, especially on the real hardware. The documentation and packages
are under way, but it's already possible to get it working.
</p>
<a name='more'></a>
<h2>
Yaml Configuration
</h2>
<p>
See below the sample configuration that you can use to boot the image on the real hardware and
get Wi-Fi working. Few notes though:
</p>
<ul>
<li>
The OS is assumed to be run from USB mass storage (see <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">root=/dev/sda1</code>
kernel parameter). Please modify it for your needs.</li>
<li>
Please use your own Wi-Fi driver instead of <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">brcmfmac</code> for the modprobe.
You can use <b>lspci</b> tool to get the required name.</li>
<li>
Please use real <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;"><ssid></code> and <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;"><password></code> values for the
wpa_supplicant configuration.</li>
<li>
I've provided few Docker images to use while the official packages are not
updated yet. You may want to create your own versions,
the related information is available in their description:
<ul>
<li>
<a href="https://hub.docker.com/r/nuald/kernel/">nuald/kernel:4.12.14-extra</a>
- LinuxKit kernel with additional Wi-Fi and USB mass storage drivers.</li>
<li>
<a href="https://hub.docker.com/r/nuald/linux-firmware/">nuald/linux-firmware:latest</a>
- the full collection of firmware blobs.</li>
<li>
<a href="https://hub.docker.com/r/nuald/wpa_supplicant/">nuald/wpa_supplicant:a5cef22bd214b2845f65636bc9cf60d805712fe5-amd64</a>
- the latest wpa_supplicant to use with the LinuxKit.
</li></ul></li></ul>
The sample configuration:
<pre><p style="margin: 0.5em;">\src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/linuxkit.yml" target="_blank">linuxkit.yml</a></p><code class="yml hljs"><span class="hljs-attr">kernel:</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">nuald/kernel:4.12.14-extra</span>
<span class="hljs-attr"> cmdline:</span> <span class="hljs-string">"console=tty0 root=/dev/sda1 rootwait vga=791"</span>
<span class="hljs-attr">init:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit/init:6fe9d31a53bbd200183bb31edd795305e868d5a7</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit/runc:a1b564248a0d0b118c11e61db9f84ecf41dd2d2a</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit/containerd:ad6710e069cb538c76314a28e09d6b49958c88e0</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">nuald/linux-firmware:latest</span>
<span class="hljs-attr">onboot:</span>
<span class="hljs-attr"> - name:</span> <span class="hljs-string">modprobe</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">linuxkit/modprobe:69494c187eddc12b3f24e4e0cbd5d3360bda3504</span>
<span class="hljs-attr"> command:</span> <span class="hljs-string">["modprobe",</span> <span class="hljs-string">"-a"</span><span class="hljs-string">,</span> <span class="hljs-string">"brcmfmac"</span><span class="hljs-string">]</span>
<span class="hljs-attr">services:</span>
<span class="hljs-attr"> - name:</span> <span class="hljs-string">wpa_supplicant</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">nuald/wpa_supplicant:a5cef22bd214b2845f65636bc9cf60d805712fe5-amd64</span>
<span class="hljs-attr"> binds:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">/etc/wpa_supplicant:/etc/wpa_supplicant</span>
<span class="hljs-attr"> command:</span> <span class="hljs-string">["/sbin/wpa_supplicant",</span> <span class="hljs-string">"-i"</span><span class="hljs-string">,</span> <span class="hljs-string">"wlan0"</span><span class="hljs-string">,</span> <span class="hljs-string">"-c"</span><span class="hljs-string">,</span> <span class="hljs-string">"/etc/wpa_supplicant/wpa_supplicant.conf"</span><span class="hljs-string">]</span>
<span class="hljs-attr"> - name:</span> <span class="hljs-string">dhcpcd</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">linuxkit/dhcpcd:d4408777ed6b6e6e562a5d4938fd09804324b33e</span>
<span class="hljs-attr"> command:</span> <span class="hljs-string">["/sbin/dhcpcd",</span> <span class="hljs-string">"wlan0"</span><span class="hljs-string">]</span>
<span class="hljs-attr"> - name:</span> <span class="hljs-string">getty</span>
<span class="hljs-attr"> image:</span> <span class="hljs-string">linuxkit/getty:bf6872ce0a9f3ab519b3e502cc41ba3958bda2a6</span>
<span class="hljs-attr"> env:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">INSECURE=true</span>
<span class="hljs-attr">files:</span>
<span class="hljs-attr"> - path:</span> <span class="hljs-string">etc/wpa_supplicant/wpa_supplicant.conf</span>
<span class="hljs-attr"> contents:</span> <span class="hljs-string">|
network={
ssid="<ssid>"
psk="<password>"
}
</span><span class="hljs-attr">trust:</span>
<span class="hljs-attr"> org:</span>
<span class="hljs-bullet"> -</span> <span class="hljs-string">linuxkit</span></code></pre>
<h2>
Usage Notes
</h2>
<p>
The configuration has been tested for the image loaded by so-called "Legacy"
BIOS mode. You may need to change the BIOS settings to disable Secure Boot and/or
enable Legacy BIOS booting options.
</p>
<p>
The image is prepared with the <b>moby</b> tool:
<pre><code class="shell hljs">moby build -format iso-bios linuxkit.yml
</code></pre></p>
<p>
Please verify the USB device you're putting image into. For macOS you can use
Disk Utility Tool:
<pre><code class="shell hljs">diskutil list
</code></pre></p>
<p>
Before writing, please unmount the device. For macOS (assuming <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">/dev/disk2</code>
the proper USB device):
<pre><code class="shell hljs">diskutil umountDisk /dev/disk2
</code></pre></p>
<p>
Next step is putting the image into USB. For both Linux and macOS the procedure the same
(please note <code class="shell hljs" style="display: inline;padding: 0px;margin: 0px;">/dev/rdisk2</code> - "r" prefix stands for the BSD "raw" device,
it's used to make the operation faster):
<pre><code class="shell hljs">sudo dd if=linuxkit.iso of=/dev/rdisk2 bs=1m
</code></pre></p>
<p>
The last step (for safety), please eject the disk (it may not be visible directly
in the system, therefore need to use CLI). For macOS (assuming the same device as above):
<pre><code class="shell hljs">diskutil eject /dev/disk2
</code></pre></p>
That's it! It may not working immediately, but it's a step forward. LinuxKit has the
great potential, and I hope you can enjoy it as much as I do.
<pre margin='0.5em'>Scalateχ \src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/LinuxKit.scalatex" target="_blank">LinuxKit.scalatex</a></pre>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-90986035782971902242017-10-01T17:24:00.000-06:002017-10-02T16:40:18.963-06:00Getting PID in Java/Scala (cross-platform)<style>/* Base16 Atelier Seaside Light - Theme */
/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside) */
/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */
/* Atelier-Seaside Comment */
.hljs-comment,
.hljs-quote {
color: #687d68;
}
/* Atelier-Seaside Red */
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #e6193c;
}
/* Atelier-Seaside Orange */
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #87711d;
}
/* Atelier-Seaside Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #29a329;
}
/* Atelier-Seaside Blue */
.hljs-title,
.hljs-section {
color: #3d62f5;
}
/* Atelier-Seaside Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #ad2bee;
}
.hljs {
display: block;
overflow-x: auto;
background: #f4fbf4;
color: #5e6e5e;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}</style>
<p>
The Scala/Java environment has a certain lack of features regarding process management -
it's in a very good shape, but sometimes we need more. It's totally
understable considering that the underlaying system is the JVM (in most cases),
however once in a while we have to leave the sandbox.
That was the case I've faced, and it got me to the problem how to get the ID of
the process I started in JVM for various operating systems.
</p>
<a name='more'></a>
<p>
The solution I've got is cross-platform, and it requires both Reflection and JNA.
It has been verified in:</p>
<ul>
<li>
macOS;</li>
<li>
Windows 10;</li>
<li>
Linux: both real (ArchLinux) and virtualized (WSL).
</li></ul>
<p>
Without further ado, let me just share the code - it's compact and pretty much
self-explanatory:
</p>
<pre><p style="margin: 0.5em;">\src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/GetPidExample.scala" target="_blank">GetPidExample.scala</a></p><code class="scala hljs">#!/usr/bin/env scalas
<span class="hljs-comment">/***
scalaVersion := "2.12.3"
libraryDependencies += "net.java.dev.jna" % "jna-platform" % "4.5.0"
*/</span>
<span class="hljs-keyword">import</span> com.sun.jna.platform.win32.<span class="hljs-type">Kernel32</span>
<span class="hljs-keyword">import</span> com.sun.jna.platform.win32.<span class="hljs-type">WinNT</span>.<span class="hljs-type">HANDLE</span>
<span class="hljs-keyword">import</span> com.sun.jna.<span class="hljs-type">Pointer</span>
<span class="hljs-keyword">import</span> sys.process.<span class="hljs-type">Process</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">getPrivateLongField</span></span>(proc: <span class="hljs-type">Any</span>, name: <span class="hljs-type">String</span>): <span class="hljs-type">Long</span> = {
<span class="hljs-keyword">val</span> privateField = proc.getClass.getDeclaredField(name)
privateField.synchronized {
privateField.setAccessible(<span class="hljs-literal">true</span>)
<span class="hljs-keyword">try</span> {
privateField.getLong(proc)
} <span class="hljs-keyword">finally</span> {
privateField.setAccessible(<span class="hljs-literal">false</span>)
}
}
}
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pidJava</span></span>(proc: <span class="hljs-type">Any</span>): <span class="hljs-type">Long</span> = {
proc <span class="hljs-keyword">match</span> {
<span class="hljs-keyword">case</span> unixProc: <span class="hljs-type">Any</span>
<span class="hljs-keyword">if</span> unixProc.getClass.getName == <span class="hljs-string">"java.lang.UNIXProcess"</span> => {
getPrivateLongField(unixProc, <span class="hljs-string">"pid"</span>)
}
<span class="hljs-keyword">case</span> windowsProc: <span class="hljs-type">Any</span>
<span class="hljs-keyword">if</span> windowsProc.getClass.getName == <span class="hljs-string">"java.lang.ProcessImpl"</span> => {
<span class="hljs-keyword">val</span> processHandle = getPrivateLongField(windowsProc, <span class="hljs-string">"handle"</span>)
<span class="hljs-keyword">val</span> kernel = <span class="hljs-type">Kernel32</span>.<span class="hljs-type">INSTANCE</span>
<span class="hljs-keyword">val</span> winHandle = <span class="hljs-keyword">new</span> <span class="hljs-type">HANDLE</span>()
winHandle.setPointer(<span class="hljs-type">Pointer</span>.createConstant(processHandle))
kernel.<span class="hljs-type">GetProcessId</span>(winHandle)
}
<span class="hljs-keyword">case</span> _ => <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-type">RuntimeException</span>(
<span class="hljs-string">"Cannot get PID of a "</span> + proc.getClass.getName)
}
}
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pid</span></span>(p: <span class="hljs-type">Process</span>): <span class="hljs-type">Long</span> = {
<span class="hljs-keyword">val</span> procField = p.getClass.getDeclaredField(<span class="hljs-string">"p"</span>)
procField.synchronized {
procField.setAccessible(<span class="hljs-literal">true</span>)
<span class="hljs-keyword">val</span> proc = procField.get(p)
<span class="hljs-keyword">try</span> {
pidJava(proc)
} <span class="hljs-keyword">finally</span> {
procField.setAccessible(<span class="hljs-literal">false</span>)
}
}
}
<span class="hljs-keyword">val</span> processName = <span class="hljs-string">"ping -c1 8.8.8.8"</span>
<span class="hljs-keyword">val</span> proc = <span class="hljs-type">Process</span>(processName).run
<span class="hljs-keyword">val</span> id = pid(proc)
println(<span class="hljs-string">s"Process '<span class="hljs-subst">$processName</span>' has PID: <span class="hljs-subst">$id</span>"</span>)</code></pre>
<pre margin='0.5em'>Scalateχ \src: <a href="https://github.com/nuald/nuald-blogspot-com/tree/master/src/main/scalatex/GetPid.scalatex" target="_blank">GetPid.scalatex</a></pre>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-78427449034128103322017-05-12T03:22:00.000-06:002017-05-12T03:22:28.812-06:00Emulating line-buffering mode (Gradle example)C printf() function doesn't flush buffers by default, and a lot of code using it may miss this, therefore creating interactivity problems to the end user. It's not specific to C though - it's about the standard I/O streams, and the problem may appear anywhere. The post is based on StackExchange discussion, so you can dig deeper if you want: <a href="https://unix.stackexchange.com/questions/25372/turn-off-buffering-in-pipe/" target="_blank">Turn off buffering in pipe</a>.<br />
<br />
<a name='more'></a>Example is based on C code that is specific for macos and has the functionality to read connected USB devices and wait for the specific one.<br />
<br />
<script src="https://gist.github.com/nuald/6b256aab4e4be3b6e0239809f67d00db.js?file=wait.c"></script><br />
The Gradle script contains two tasks - <b>runSimple</b> task that runs the program naively, and <b>run</b> task that using "script" utility to emulate the line-buffering mode.<br />
<br />
<script src="https://gist.github.com/nuald/6b256aab4e4be3b6e0239809f67d00db.js?file=build.gradle"></script><br />
If you use <b>runSimple</b>, then there will be no output until the device is actually connected (and no output at all if you use Ctrl-C to break the execution):<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">~/ > gradle runSimple</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Starting a Gradle Daemon, 1 busy Daemon could not be reused, use --status for details</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">:compile UP-TO-DATE</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">:runSimple</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Waiting for Cruzer</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Got 4 connected devices. Still waiting...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Got 4 connected devices. Still waiting...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">BUILD SUCCESSFUL</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Total time: 7.851 secs</span><br />
<br />
However, <b>run</b> works correctly and shows the output as needed. The best solution though would be using fflush() function, but unfortunately it's not always possible (especially if we use 3rd party program that we can't recompile).<br />
<br />Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-34258484069714766472017-05-11T22:14:00.000-06:002017-05-11T22:32:57.740-06:00Xcode build settings<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_RPMy8X22wubXEaSIrsn9IOREoCgWRf8-T9blT2-nbae5TCUKHo8h5iuMHLZ9pt24q4XopLjFi0banNNscWwKQKkxVIv78HYPQgEZEQI3rid_d_6vPK5UzsNRAe-IwvmbPFzH88UhMug/s1600/Screen+Shot+2017-05-12+at+2.31.55+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_RPMy8X22wubXEaSIrsn9IOREoCgWRf8-T9blT2-nbae5TCUKHo8h5iuMHLZ9pt24q4XopLjFi0banNNscWwKQKkxVIv78HYPQgEZEQI3rid_d_6vPK5UzsNRAe-IwvmbPFzH88UhMug/s320/Screen+Shot+2017-05-12+at+2.31.55+PM.png" width="320" /></a></div>
<br />
Xcode build locations may mislead developers hoping to fully control the output of the IDE. While we can use CONFIGURATION_BUILD_DIR for the xcodebuild tool during continuous integration, IDE generates files in different locations, and sometimes we want to control it too.<br />
<br />
<a name='more'></a><br />
<br />
First, misleading part:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdl2iA5g9ENhslyDLaNBfd3apjtcODtzYg_WvJibGrFfc-n1jW0Ewh082OSLSCmLWvAzsLYM3aWqQcqs9uoN-mdaf_-4j2MXo4O3Icn7uvOsy7plZQ_MNAuXkuYtZkvoQf2cVUvo4fMb0/s1600/Screen+Shot+2017-05-12+at+1.41.05+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="168" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdl2iA5g9ENhslyDLaNBfd3apjtcODtzYg_WvJibGrFfc-n1jW0Ewh082OSLSCmLWvAzsLYM3aWqQcqs9uoN-mdaf_-4j2MXo4O3Icn7uvOsy7plZQ_MNAuXkuYtZkvoQf2cVUvo4fMb0/s640/Screen+Shot+2017-05-12+at+1.41.05+PM.png" width="640" /></a></div>
<br />
<b>Per-configuration Build Products Path</b> states "build/Debug-iphoneos", however it's not true - the output is controlled by the workspace settings (File -> Workspace Settings... -> Advanced):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCqlIQG_67zvVI9hR8EtybssbCbvrMdmTggby918OCFpYP6f9Aq7HeUr-YyPsRi41UP-z010VKsjuiITuuLcmZKlKwvgrTXCjRfdEyqJatxRWirvhHRVB6PgPUwbMo4qn0uo3c75EMStU/s1600/Screen+Shot+2017-05-12+at+1.45.00+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCqlIQG_67zvVI9hR8EtybssbCbvrMdmTggby918OCFpYP6f9Aq7HeUr-YyPsRi41UP-z010VKsjuiITuuLcmZKlKwvgrTXCjRfdEyqJatxRWirvhHRVB6PgPUwbMo4qn0uo3c75EMStU/s320/Screen+Shot+2017-05-12+at+1.45.00+PM.png" width="320" /></a></div>
<br />
However, using workspace settings is not flexible enough and may have some issues. In particular, we can't save these settings into the version control: it's per-user basis, and we do not usually want to save <b>xcuserdata</b> directory into the VCS.<br />
<br />
It looks like the Xcode GUI for the Build Locations section is not always working properly, and it shows the build location for the legacy mode by default (Xcode prior version 4) where SYMROOT settings (<b>Build Product Path</b>) was used. However, the documentation for settings is correct:<br />
<br />
<div style="margin-left: 20pt;">
<b>Per-configuration Build Products Path</b><br />
<br />
CONFIGURATION_BUILD_DIR <br />
<br />
The base path where build products will be placed during a build for a given configuration. By default this is set to `$(BUILD_DIR)/$(CONFIGURATION)`.</div>
<br />
Workspace settings does set up this BUILD_DIR and by modifying this particular setting, we can control the build products output. The same about intermediate build files:<br />
<br />
<div style="margin-left: 20pt;">
<b>Per-configuration Intermediate Build Files Path</b><br />
<br />
CONFIGURATION_TEMP_DIR <br />
<br />
The base path where intermediates will be placed during a build for a given configuration. By default this is set to `$(PROJECT_TEMP_DIR)/$(CONFIGURATION)`.</div>
<br />
BUILD_DIR and PROJECT_TEMP_DIR do not have entries in the Build Settings page, but we can set those up with User-Defined Settings:<br />
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">BUILD_DIR = $(SRCROOT)/Build/Products</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">PROJECT_TEMP_DIR = $(SRCROOT)/Build/Intermediates</span></div>
<div>
<br /></div>
<div>
Please note that you can set up any paths there, the example refers to the path relative to the project root with the names consistent with the default ones.</div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-42175362732319529652017-03-31T18:23:00.000-06:002017-03-31T18:23:35.819-06:00Thunderbird Grammar Checker 0.7 has been releasedLink: <a href="https://addons.mozilla.org/en-US/thunderbird/addon/grammar-checker/">https://addons.mozilla.org/en-US/thunderbird/addon/grammar-checker/</a><br />
<br />
This is a maintenance release that has fixed the compatibility issues with the LanguageTool API v2 (the current one) and the new Thunderbird API. No functionality has been added, but now the add-on actually works.<br />
<br />
Development of the add-on have been moved to Github: <a href="https://github.com/nuald/thb-gramchecker">https://github.com/nuald/thb-gramchecker</a><br />
<br />
Unfortunately, I can't actively maintain the add-on, but all pull requests are welcomed.Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-64663392941219758232017-01-04T00:43:00.000-07:002017-01-04T00:43:48.108-07:00Gradle task for launching Android simulatorAndroid build toolchain, albeit being powerful, sometimes lacks some essential functionality required for the modern software development. In particular, running instrumented unit-tests in the CI environment: those tests require a device or a simulator running, however developers need some scripting to make it work.<br />
<br />
To simplify the task, I've created a Gradle task that verifies if any Android device or simulator is connected. If not, the task launches a simulator with the first AVD in the list (or fails if no AVDs are found). It's up to you to make the dependencies for the connectedCheck task. The sample output:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">> ./gradlew connect --info</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">...</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">:AndroidSampleApp:connect (Thread[main,5,main]) started.</span><br />
<span style="font-family: Courier New, Courier, monospace;">:AndroidSampleApp:connect</span><br />
<span style="font-family: Courier New, Courier, monospace;">Executing task ':AndroidSampleApp:connect' (up-to-date check took 0.001 secs) due to:</span><br />
<span style="font-family: Courier New, Courier, monospace;"> Task has not declared any outputs.</span><br />
<span style="font-family: Courier New, Courier, monospace;">Detecting devices...</span><br />
<span style="font-family: Courier New, Courier, monospace;">Launching emulator for Nexus_5_API_25 ...</span><br />
<span style="font-family: Courier New, Courier, monospace;">:AndroidSampleApp:connect (Thread[main,5,main]) completed. Took 14.186 secs.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">BUILD SUCCESSFUL</span><br />
<br />
The code (just add it into the build.gradle of the module):<br />
<br />
<script src="https://gist.github.com/nuald/6b0408cf23039631b7db76347d405419.js"></script><br />
<br />
<br />Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-78265692888280267022016-12-07T05:12:00.000-07:002016-12-07T05:12:59.441-07:00Simple HTTP/HTTPS server on Scala/AkkaLet me present a simple <a href="https://gist.github.com/nuald/cd058bd2ef1c755664f530fd64198574" target="_blank">Scala script</a> to serve local files over HTTP or HTTPS. I've used Scala/Akka to get the good performance and to play around with the programming language I really like.<br />
<br />
<h4>
Toolchain</h4>
<br />
We need Scala, <a href="http://www.scala-sbt.org/" target="_blank">SBT</a> (Scala interactive build tool), <a href="http://www.foundweekends.org/conscript/" target="_blank">Conscript</a> (a distribution mechanism for Scala apps) and <a href="http://www.scala-sbt.org/0.13/docs/Scripts.html" target="_blank">Scalas</a> (the script runner for Scala). Conscript is needed only to install Scalas here, so you may want to install the runner manually (see the Scalas link above) if you don't want to install Conscript.<br />
<br />
For Mac OS X we're going to use <a href="http://brew.sh/" target="_blank">Homebrew</a> to install Scala and SBT:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> $ brew install scala sbt</span><br />
<br />
Next step is Conscript (please follow the <a href="http://www.foundweekends.org/conscript/setup.html" target="_blank">installation instructions</a> on the official site):<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> $ wget https://dl.bintray.com/foundweekends/maven-releases/org/foundweekends/conscript/conscript_2.11/0.5.1/conscript_2.11-0.5.1-proguard.jar</span><br />
<span style="font-family: Courier New, Courier, monospace;"> $ java -jar conscript_2.11-0.5.1-proguard.jar</span><br />
<br />
Next step is installing Scalas (please follow the <a href="http://www.scala-sbt.org/0.13/docs/Scripts.html" target="_blank">installation instructions</a> on the official site):<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> $ cs sbt/sbt --branch 0.13.13</span><br />
<br />
It may show some errors, please dismiss those.<br />
<br />
<h4>
HTTPS self-signed certificate</h4>
<br />
Java has the handy tool to generate the certificate, just call it with the required parameters (in the example it creates a RSA certificate valid for 365 days):<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> $ keytool -genkey -keyalg RSA -validity 365 -keystore /path/to/file -storetype PKCS12</span><br />
<br />
where /path/to/file is the keystore file you can use with the HTTPS server.<br />
<br />
Please note that the generated certificate is not trusted by browsers. If you have a legit public website, you can generate a certificate for free using <a href="https://letsencrypt.org/" target="_blank">Let's Encrypt</a> service.<br />
<br />
<h4>
The script</h4>
<br />
Please note that SBT will download the dependencies, therefore the first start may take a while. After that the using of the script is straightforward (please use "--help" command if you're lost).<br />
<br />
Sample output:<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> $ /serve.scala --keystore=server.p12 --password=89WSTfO</span><span style="font-family: "Courier New", Courier, monospace;"> --port=9000</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">[INFO] [12/07/2016 22:02:59.682] [run-main-0] [http(akka://sys)] scala version 2.12.0</span><br />
<span style="font-family: Courier New, Courier, monospace;">[INFO] [12/07/2016 22:02:59.891] [run-main-0] [http(akka://sys)] start: https://192.168.1.26:9000/</span><br />
<span style="font-family: Courier New, Courier, monospace;">[INFO] [12/07/2016 22:03:33.832] [sys-akka.actor.default-dispatcher-9] [http(akka://sys)] GET /</span><br />
<span style="font-family: Courier New, Courier, monospace;">[INFO] [12/07/2016 22:03:34.121] [sys-akka.actor.default-dispatcher-2] [http(akka://sys)] GET /favicon.ico</span><br />
<br />
The code:<br />
<br />
<script src="https://gist.github.com/nuald/cd058bd2ef1c755664f530fd64198574.js"></script><br />
<br />Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-87550182772063336482016-11-11T00:14:00.000-07:002016-11-11T00:14:01.159-07:00Android screen capture with GIF formatGIF format is still here, and it's a handy way to make small videos. It's very easy to do despite of all ads telling us to buy their commercial software, and I'm going to describe the tools we need.<br />
<br />
First, we need to capture the video of the screen. For Android, you can use <a href="https://play.google.com/store/apps/details?id=com.blogspot.byterevapps.lollipopscreenrecorder&hl=en">ADV Screen Recorder</a>, but surely there are tools for any platform. Please verify the recorder settings (you may want to disable mic recording and change the output folder to be on an external SD).<br />
<br />
Second, after we recorded the video, we need to convert it into GIF-format. For that we're going to use FFMpeg tool (on Mac, just use "brew install ffmpeg").<br />
<br />
GIF uses 256 colors, so we need to create an auxiliary palette file. Therefore, the converting process takes two steps: 1) create a palette, 2) use a palette and an input file to create the output GIF. The commands for that:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">$ ffmpeg -i <b>input.mp4</b> -vf fps=10,scale=320:-1:flags=lanczos,palettegen <b>palette.png</b></span><br />
<span style="font-family: Courier New, Courier, monospace;">$ ffmpeg -i <b>input.mp4</b> -i <b>palette.png</b> -filter_complex "fps=10,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" <b>output.gif</b></span><br />
<br />
output.gif is the resulting file. It's possible you may need to modify ffmpeg settings, but in most cases it's not needed. Have fun!Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-86777497323624317342016-09-27T19:34:00.000-06:002016-09-27T19:34:32.703-06:00shelljs node module Let me share a little gem I've found: <a href="http://documentup.com/shelljs/shelljs">shelljs node module</a>. I was working on updating my Cordova hooks to work with Windows environment and had to replace my bash script with the corresponding JS code. Unfortunately, the node modules I've found were disappointing, especially copying the files. Surely I could write the copying code by myself, but I supposed that in 2016 year one is not required to do such menial tasks.<br />
<br />
The biggest problem for the modules I've tried was the glob matching - I have to copy files from the sibling directory, and modules couldn't find it (looks like they don't parse ".." notation). I've tried fs.extra, fs-extra, ncp, filecopy, cp, but no luck. I guess I could find the good module after all, but after I've discovered shelljs I don't need it anymore anyway. With shelljs porting the code was a piece of cake - see the results below (the original and the new scripts).<br />
<br />
<script src="https://gist.github.com/nuald/3a7df998b2a5e2ea008493c651ea53eb.js"></script>Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-49265218541635768462016-07-15T20:44:00.000-06:002017-09-20T00:58:44.201-06:00Software licensingFor my software projects I've always preferred the GPL license, but nowadays its time has gone, at least for me. The IT industry is mature enough, and all copyleft (and copyright) licenses create more problems than advantages. Few examples:<br />
<br />
<ul>
<li><b>Inability to use the code in the commercial applications.</b> Commercial applications are infamous for their poor quality, security-by-obscurity and other issues. I guess we all agree that in the perfect world the applications should be polished before releasing, but free market dictates faster release cycles, and therefore the overall poor quality of the software. At the end, it influences us all, directly or indirectly (commercial OSes, routers, cloud solutions and other infrastructure elements), and by lifting the software licenses restrictions we may help the commercial developers to use our code in their projects (instead of implementing their own, in many cases, inefficient, solutions). For example, because of the<i> readline</i> library restrictions, I had to implement my own version for one of the commercial projects I've worked on, and it went pretty bad, because I had very narrow timeline for the project. At the end, I wasted time and clients got an inefficient and error-prone solution.</li>
<li><b>Developers contribute back regardless of the license.</b> Many developers even don't bother with the licenses (unless the lawyers require them to do it). However, contributing helps everyone - developers don't need to keep their own patches for the used software, and the software creators are getting feedbacks and new patches to improve the quality of the code. The only reason to prevent contributing (in my practice) are restrictive licenses which may require that all the code copyrights should be waived, or the whole codebase should be open. Frankly, I don't see people around who are not willing to contribute back to public, we all see advantages of it.</li>
<li><b>Judges are getting better.</b> Oracle v Google case is a good example: <a href="http://arstechnica.com/tech-policy/2016/05/google-wins-trial-against-oracle-as-jury-finds-android-is-fair-use/">Google beats Oracle—Android makes “fair use” of Java APIs</a> One of the biggest reason for software licensing is a protection from patent trolls, but I think the situation is getting better. However, we still need to be careful, and have to use the proper licenses. Nevertheless, it doesn't mean that we need to escalate the situation by using the restrictive licenses. There is a good explanation about the legal hazards of open licenses: <a href="http://opensource.stackexchange.com/a/1381">How can I place software in the public domain</a></li>
</ul>
<div>
Following the advice in the last link, all my projects are going to have <a href="http://unlicense.org/" target="_blank">The Unlicense</a>. And let me quote their reasons to create the license:</div>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: "helvetica neue" , "helvetica" , "arial" , "geneva" , sans-serif; font-size: 13px;">You have more important things to do than enriching lawyers or imposing petty restrictions on users of your code. How often have you passed up on utilizing and contributing to a great software library just because its </span><a href="http://www.opensource.org/" style="color: #990000; font-family: 'Helvetica Neue', Helvetica, Arial, Geneva, sans-serif; font-size: 13px;">open source</a><span style="background-color: white; font-family: "helvetica neue" , "helvetica" , "arial" , "geneva" , sans-serif; font-size: 13px;"> license was not compatible with your own preferred </span><a href="http://www.opensource.org/licenses/alphabetical" style="color: #990000; font-family: 'Helvetica Neue', Helvetica, Arial, Geneva, sans-serif; font-size: 13px;">flavor</a><span style="background-color: white; font-family: "helvetica neue" , "helvetica" , "arial" , "geneva" , sans-serif; font-size: 13px;"> of open source? How many precious hours of your life have you spent deliberating how to license your software or worrying about licensing compatibility with other software? You will never get those hours back, but here's your chance to start cutting your losses. Life's too short, let's get back to coding.</span></blockquote>
<div>
<br /></div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-29948469892301419462014-05-14T08:30:00.002-06:002016-11-11T00:14:16.186-07:00glSatellite Demo<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKmEvctmHSRSIXV-cBM-u0miYi5mIZ37UeNN_8Fm1XZK1HYEqLPamgLHV6maVqEeqVXmRuzkMgv5E_J5gm9cHtJWcHrU-3aH9wmEDj-cFYgi0rb065TXfBh4DyCkIYF_JRqV7ObBljG6s/s1600/unnamed.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKmEvctmHSRSIXV-cBM-u0miYi5mIZ37UeNN_8Fm1XZK1HYEqLPamgLHV6maVqEeqVXmRuzkMgv5E_J5gm9cHtJWcHrU-3aH9wmEDj-cFYgi0rb065TXfBh4DyCkIYF_JRqV7ObBljG6s/s1600/unnamed.png" width="222" /></a></div>
During my research for 3D graphics application development I've noticed that a lot of tutorials and advices on the Internet are obsolete, imcomplete or even incorrect. That's why I've decided to create an open source technological demo of OpenGL for Android platform. That's just a demo, so it doesn't have any particular useful purpose, but shows the methods and algorithms to accomplish certain tasks.<br />
<br />
For now the application reads public NORAD satellite database and shows the satellite positions and altitude related to Earth in the form of beams (the height of the beam represents the relative altitude).<br />
<br />
The 3D features present:<br />
- OpenGL ES 2;<br />
- rotating, multi-touch zooming and moving the globe;<br />
- stars on a fixed background;<br />
- each satellite is represented by a beam with a unique color;<br />
- identifying the corresponding satellite if a beam has been tapped (based on color picking method).<br />
<br />
Additionally to 3D features presented the application is:<br />
- using NDK with C++11 features compiled by LLVM clang;<br />
- mixing Java views with OpenGL views (Java is used for settings activity, information and ads panels);<br />
- using Android custom message queue to send asynchronous messages from Java to JNI layer;<br />
- parsing public NORAD satellite database (SGP4/SDP4 orbital models are supported).<br />
<br />
Google Play app: <a href="https://play.google.com/store/apps/details?id=ca.raido.glSatelliteDemo">https://play.google.com/store/apps/details?id=ca.raido.glSatelliteDemo</a><br />
<br />
Source code is hosted on SourceForge at <a href="https://sourceforge.net/projects/glsatellitedemo/">https://sourceforge.net/projects/glsatellitedemo/</a><br />
<br /></div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-9448035107079464552013-09-20T10:39:00.004-06:002013-09-20T10:39:42.118-06:00wxWidgets and OpenSSL<div dir="ltr" style="text-align: left;" trbidi="on">
Securing connections is always a good idea and OpenSSL provides a number of handy ways to do it. Blocking OpenSSL API is the most easiest to use and it could provide a working solution in no time, however it doesn't work with wxWidgets library so smooth as one may hope. I spent some time to find the reason why my application doesn't work as intended (plain sockets work just fine though) and had to do a little research on that matter.<br />
<br />
Regardless of the flags you provide to wxSocketServer it always creates a socket on the new connection with that <b>ioctl</b> call (UnblockAndRegisterWithEventLoop function in include/wx/unix/private/sockunix.h):<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">ioctl(m_fd, FIONBIO, &trueArg);</span><br />
<br />
Basically, it just makes the socket non-blocking. I don't know is it a bug or feature, but we have it now and have to deal with it. Naturally all blocking OpenSSL API doesn't work and we have to develop the code using non-blocking behaviour. However, it's pretty easy to use if we <i>emulate</i> the blocking pattern in the code.<br />
<br />
First, we have to guarantee that the created socket doesn't accept events and works with blocking calls correctly:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span><span style="font-family: "Courier New",Courier,monospace;">auto socket = server->Accept(false);<br />if (!socket) {<br /> return;<br />}<br />//Notify() cannot be called in thread context. We have to detach from main loop</span><br />
<span style="font-family: "Courier New",Courier,monospace;">//before switching thread contexts.<br />socket->Notify(false);<br />socket->SetFlags(wxSOCKET_WAITALL|wxSOCKET_BLOCK);<br /><br />auto thread = new WorkerThread(socket);<br />if (thread->Create() != wxTHREAD_NO_ERROR) {<br /> wxLogError(wxT("Can't create thread!"));<br />} else if (thread->Run() != wxTHREAD_NO_ERROR) {<br /> wxLogError(wxT("Can't run thread!"));<br />}</span><br />
<br />
In the thread we process the OpenSSL calls with that helper function:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">int NonBlockingSslCall(SSL* ssl, wxSocketBase* socket,<br /> std::function<int ()> func) {<br /> int ret;<br /> for(;;) {<br /> ret = func();<br /> if (ret < 0 && socket->IsOk()) {<br /> switch (SSL_get_error(ssl, ret)) {<br /> case SSL_ERROR_WANT_READ:<br /> socket->WaitForRead();<br /> break;<br /> case SSL_ERROR_WANT_WRITE:<br /> socket->WaitForWrite();<br /> break;<br /> default:<br /> throw SslException("Fatal error occurred", ssl, ret);<br /> }<br /> continue;<br /> }<br /> break;<br /> }<br /> return ret;<br />}</span><br />
<br />
In few words this function do the OpenSSL call (see the func variable) and check for <span style="font-family: "Courier New",Courier,monospace;">SSL_ERROR_WANT_READ/</span><span style="font-family: "Courier New",Courier,monospace;">SSL_ERROR_WANT_WRITE</span> error code. If it happens, it blocks the program waiting for the corresponding data.<span style="font-family: "Courier New",Courier,monospace;"></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">Here comes C++11 handy features - and we use lambda to do the required "blocking" OpenSSL call:</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">int r = NonBlockingSslCall(ssl, socket,<br /> [ssl] () { return SSL_accept(ssl); });<br />if (r <= 0) {<br /> throw SslException("The SSL accept operation failed", ssl, r);<br />}</span><br />
<br />
One more notice here - lambda doesn't work with moveable types, so you have to use the plain pointer as a catching parameter:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">std::unique_ptr<BIO, decltype(&BIO_free)> io(BIO_new(BIO_f_buffer()), BIO_free);</span><br />
<span style="font-family: "Courier New",Courier,monospace;">...</span><br />
<span style="font-family: "Courier New",Courier,monospace;">BIO* io_ptr = io.get();</span><br />
<span style="font-family: "Courier New",Courier,monospace;">r = NonBlockingSslCall(ssl, socket,<br /> [io_ptr, buf_ptr, out_ptr] () {<br /> int sz = BIO_gets(io_ptr, buf_ptr, BUFSIZZ - 1);<br /> if (sz > 0) {<br /> out_ptr->Write(buf_ptr, sz);<br /> }<br /> return sz;<br /> }<br /> );</span><br />
<br />
The examples above don't provide full listings of the code, but I hope the idea is clear. Surely, there are more elegant (but I guess more verbose) solutions like by using wxWidgets events, but the method above works for me without any problems. I hope that will help you too on the rough road of securing your networking software.</div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0tag:blogger.com,1999:blog-3721596730656231421.post-68224917843050089462013-06-14T15:02:00.001-06:002013-06-14T15:02:09.864-06:00Memory reclaiming in Python <div dir="ltr" style="text-align: left;" trbidi="on">
Running GC-based languages on embedded systems always give a challenge to limit the physical memory amount taken by the processes. Python scripting is obviously a good example what would happen if you use long-running processes and which problems you could face. Let me show my research and the way I've used to fix the memory consumption.<br />
<br />
First, a trivial example which is used in the Internet, and which is actually <b>wrong</b> and doesn't show the problem:<br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace;">import gc</span><br />
<span style="font-family: Courier New, Courier, monospace;">import os</span><br />
<span style="font-family: Courier New, Courier, monospace;">iterations = 1000000</span><br />
<span style="font-family: Courier New, Courier, monospace;">pid = os.getpid()</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">def rss():</span><br />
<span style="font-family: Courier New, Courier, monospace;"> with open('/proc/%d/status' % pid, 'r') as f:</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for line in f:</span><br />
<span style="font-family: Courier New, Courier, monospace;"> if 'VmRSS' in line:</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return line</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">def main():</span><br />
<span style="font-family: Courier New, Courier, monospace;"> print 'Before allocating ', rss(),</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> l = []</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for i in xrange(iterations):</span><br />
<span style="font-family: Courier New, Courier, monospace;"> l.append({})</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> print 'After allocating ', rss(),</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # Ignore optimizations, just try to free whatever possible</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # First kill</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for i in xrange(iterations):</span><br />
<span style="font-family: Courier New, Courier, monospace;"> l[i] = None</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # Second kill</span><br />
<span style="font-family: Courier New, Courier, monospace;"> l = None</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # Control shot</span><br />
<span style="font-family: Courier New, Courier, monospace;"> gc.collect()</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> print 'After free ', rss(),</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">if __name__ == '__main__':</span><br />
<span style="font-family: Courier New, Courier, monospace;"> main()</span><br />
<br />
<br />
Running it shows that everything is fine (here and below I use Python 2.6.8):<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">Before allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 3344 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 149216 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After free VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 4748 kB</span><br />
<div>
<br /></div>
<br />
But let's use a dictionary object instead of the list now:<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">import gc</span><br />
<span style="font-family: Courier New, Courier, monospace;">import os</span><br />
<span style="font-family: Courier New, Courier, monospace;">iterations = 1000000</span><br />
<span style="font-family: Courier New, Courier, monospace;">pid = os.getpid()</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">def rss():</span><br />
<span style="font-family: Courier New, Courier, monospace;"> with open('/proc/%d/status' % pid, 'r') as f:</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for line in f:</span><br />
<span style="font-family: Courier New, Courier, monospace;"> if 'VmRSS' in line:</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return line</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">def main():</span><br />
<span style="font-family: Courier New, Courier, monospace;"> print 'Before allocating ', rss(),</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> l = {}</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for i in xrange(iterations):</span><br />
<span style="font-family: Courier New, Courier, monospace;"> l[i] = {}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> print 'After allocating ', rss(),</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # Ignore optimizations, just try to free whatever possible</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # First kill</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for i in xrange(iterations):</span><br />
<span style="font-family: Courier New, Courier, monospace;"> l[i] = None</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # Second kill</span><br />
<span style="font-family: Courier New, Courier, monospace;"> l.clear()</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # Third kill</span><br />
<span style="font-family: Courier New, Courier, monospace;"> l = None</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # Control shot</span><br />
<span style="font-family: Courier New, Courier, monospace;"> gc.collect()</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> print 'After free ', rss(),</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">if __name__ == '__main__':</span><br />
<span style="font-family: Courier New, Courier, monospace;"> main()</span><br />
<br />
<br />
Let's run it:<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">Before allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 3348 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 179800 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After free VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 155300 kB</span><br />
<div>
<br /></div>
<br />
<br />
That doesn't look good, isn't it? Obviously Python manipulates dictionaries in a different way, but unfortunately it's not the good news for us.<br />
<br />
The first guess is that Python uses <a href="http://docs.python.org/3/whatsnew/2.3.html#pymalloc-a-specialized-object-allocator">PyMalloc</a> which doesn't free the memory but reuse it later. It's fine for the desktop/server systems, but not so good for the embedded systems, because other processes have needs in memory too. Please notice that an operating system can behave differently for the embedded systems and might not send special signals to the processes to reclaim the memory (as in my case). Also PyMalloc's freelist memory pool for integers and floats is never claimed back to the operating system at all.<br />
<br />
The second attempt is to recompile Python without PyMalloc:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">$ ./configure --without-pymalloc</span><br />
<span style="font-family: Courier New, Courier, monospace;">$ make -sj4</span><br />
<span style="font-family: Courier New, Courier, monospace;">$ ./python test.py</span><br />
<br />
<span style="font-family: Courier New, Courier, monospace;">Before allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 3304 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 180112 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After free VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 155748 kB</span><br />
<div>
<br /></div>
<br />
Mostly the same numbers, so looks like PyMalloc has nothing to do with it. And actually it's true, this it not PyMalloc behaviour, but glibc. See the bug: <a href="http://bugs.python.org/issue11849">http://bugs.python.org/issue11849</a> In few words, if the process allocates a lot of small objects, glibc uses different approach and to enforce releasing the memory pool one should use malloc_trim(). The patch has been applied to Python 3.3 to improve the situation (the sample code should be slightly modified to be compatible with Py3k, I skipped it here):<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">$ python 3.3 test.py</span><br />
<br />
<span style="font-family: Courier New, Courier, monospace;">Before allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 4780 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 193776 kB</span><br />
<span style="font-family: Courier New, Courier, monospace;">After free VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 83288 kB</span><br />
<br />
But as you can see, the problem still exists. Using memory_trim() manually in the Python memory allocator doesn't sound like a good solution, however another solution can be applied - to use not glibc memory allocator, but 3rd-side one. In my cases it's jemalloc:<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">$ sudo apt-get install libjemalloc1</span><br />
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$ LD_PRELOAD=/usr/local/lib/libjemalloc.so ./python test.py </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Before allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 3692 kB</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">After allocating VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 197780 kB</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">After free VmRSS:<span class="Apple-tab-span" style="white-space: pre;"> </span> 3984 kB</span></div>
</div>
<div>
<br /></div>
<br />
<br />
Presto, problem solved! It wouldn't be so easy for certain environments, especially with prefixed API, but it's a start. Also the custom memory allocator could be applied for different processes, not only Python, and eventually it can save a lot of memory in your embedded system.<br />
<br /></div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com1tag:blogger.com,1999:blog-3721596730656231421.post-43803248204853521292013-04-05T11:25:00.000-06:002013-04-05T11:25:58.331-06:00Minimalistic Linux threading<div dir="ltr" style="text-align: left;" trbidi="on">
Embedded system development always poses certain challenges and one of them is the threading support. Including POSIX threads (pthread) is not an option sometimes, but fortunately Linux kernel allows to make LWP (light-weight processes) which are similar to threads. The key function here is the <b>clone</b> function. <b>fork</b> internally uses <b>clone</b> too, but <b>clone</b> allows to create child processes with the different settings including <b>CLONE_VM</b> (share the memory between parent and children processes) which is essential for threading.<br />
<br />
Without further ado let me give an example, and I'll describe key pieces below:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">//Linux light-weight processes usage example</span><br />
<span style="font-family: Courier New, Courier, monospace;">#define _GNU_SOURCE</span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <fcntl.h></span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <sched.h></span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <stdarg.h></span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <stdio.h></span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <stdlib.h></span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <sys/syscall.h></span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <sys/types.h></span><br />
<span style="font-family: Courier New, Courier, monospace;">#include <sys/wait.h></span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;">// Default thread number</span><br />
<span style="font-family: Courier New, Courier, monospace;">#define THREAD_NUM 5</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;">int message(const char *format, ...) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> va_list arg_list;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> va_start(arg_list, format);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> // Unbuffered output to avoid the threading artefacts</span><br />
<span style="font-family: Courier New, Courier, monospace;"> vfprintf(stderr, format, arg_list);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> va_end(arg_list);</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;">// The child thread will execute this function</span><br />
<span style="font-family: Courier New, Courier, monospace;">int thread_function(void* argument) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> pid_t tid = (pid_t)syscall(SYS_gettid);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> message("Thread number %d (TID: %d) has been called\n",</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (int)argument, tid);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return 0;</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;">int main() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> struct rlimit rlim;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> void* stack_list[THREAD_NUM];</span><br />
<span style="font-family: Courier New, Courier, monospace;"> pid_t tid_list[THREAD_NUM];</span><br />
<span style="font-family: Courier New, Courier, monospace;"> int i = 0, stack_size;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> int ret = getrlimit(RLIMIT_STACK, &rlim);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> if (ret == -1) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> perror("getrlimit: could not get stack size limit");</span><br />
<span style="font-family: Courier New, Courier, monospace;"> exit(1);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"> stack_size = rlim.rlim_cur;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> message("Stack size limit: %d bytes\n", stack_size);</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> for (; i < THREAD_NUM; ++i) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> void* stack;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> pid_t tid;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> // Allocate the stack</span><br />
<span style="font-family: Courier New, Courier, monospace;"> stack = malloc(stack_size);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> if (stack == 0) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> perror("malloc: could not allocate stack");</span><br />
<span style="font-family: Courier New, Courier, monospace;"> exit(1);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> // Call the clone syscall to create the child thread</span><br />
<span style="font-family: Courier New, Courier, monospace;"> tid = clone(&thread_function,</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (char*) stack + stack_size,</span><br />
<span style="font-family: Courier New, Courier, monospace;"> SIGCHLD | CLONE_SIGHAND | CLONE_VM,</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (void*)i);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> message("%d-th child thread has been created (TID: %d)\n", i, tid);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> if (tid == -1) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> perror("clone");</span><br />
<span style="font-family: Courier New, Courier, monospace;"> exit(2);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> // Save the thread information</span><br />
<span style="font-family: Courier New, Courier, monospace;"> tid_list[i] = tid;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> stack_list[i] = stack;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> // Clean-up phase</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for (i = 0; i < THREAD_NUM; ++i) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> pid_t pid;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> // Wait for the child thread to exit</span><br />
<span style="font-family: Courier New, Courier, monospace;"> pid = waitpid(tid_list[i], 0, 0);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> if (pid == -1) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> perror("waitpid");</span><br />
<span style="font-family: Courier New, Courier, monospace;"> exit(3);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> // Free the stack</span><br />
<span style="font-family: Courier New, Courier, monospace;"> free(stack_list[i]);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br />
</span> <span style="font-family: Courier New, Courier, monospace;"> return 0;</span><br />
<span style="font-family: Courier New, Courier, monospace;">} </span><br />
<br />
First, we're getting stack size for the children. You can use any hardcoded value (if for example, <b>getrlimit</b> doesn't work correctly for your version of the kernel).<br />
<br />
Second, we're calling <b>clone</b> with<span style="font-family: inherit;"> <b>SIGCHLD | CLONE_SIGHAND | CLONE_VM </b>flags. <b>SIGCHLD</b> is used for later <b>waitpid</b> call, if you don't want/need to wait for the children, you can omit these flags (however, you have to somehow free the allocated memory after the children exit).</span><br />
<span style="font-family: inherit;"><br />
</span> <span style="font-family: inherit;">One more important function here is<span style="font-family: inherit;"> </span></span><span style="font-family: inherit;"><b>gettid</b> (used as <b>syscall(SYS_gettid)</b>) - it allows the child to get its thread id. It's the same as the <b>clone</b> function result, and can be used for more complicated thread management.</span><br />
<span style="font-family: inherit;"><br />
</span> <span style="font-family: inherit;">Of course, the effective thread management requires much more functionality (locking, synchronizing, atomic operations etc) and it's much easier to use existing thread frameworks like POSIX threads, however, in certain cases it's impossible due to platform limitations, but <b>clone</b> is always available in the Linux kernel and can lend a hand if needed.</span></div>
Alexander Slesarevhttp://www.blogger.com/profile/05637529008894979993noreply@blogger.com0