1 # read: analogously to write, support reading from in-memory streams in 2 # addition to file descriptors. 3 # 4 # We can pass it either a file descriptor or an address to a stream. If a 5 # file descriptor is passed in, we _read from it using the right syscall. If a 6 # stream is passed in (a fake file descriptor), we read from it instead. This 7 # lets us initialize input for tests. 8 # 9 # A little counter-intuitively, the output of 'read' ends up in.. a stream. So 10 # tests end up doing a redundant copy. Why? Well, consider the alternatives: 11 # 12 # a) Reading into a string, and returning a pointer to the end of the read 13 # region, or a count of bytes written. Now this count or end pointer must be 14 # managed separately by the caller, which can be error-prone. 15 # 16 # b) Having 'read' return a buffer that it allocates. But there's no way to 17 # know in advance how large to make the buffer. If you read less than the 18 # size of the buffer you again end up needing to manage initialized vs 19 # uninitialized memory. 20 # 21 # c) Creating more helpful variants like 'read-byte' or 'read-until' which 22 # also can take a file descriptor or stream, just like 'write'. But such 23 # primitives don't exist in the Linux kernel, so we'd be implementing them 24 # somehow, either with more internal buffering or by making multiple 25 # syscalls. 26 # 27 # Reading into a stream avoids these problems. The buffer is externally 28 # provided and the caller has control over where it's allocated, its lifetime, 29 # and so on. The buffer's read and write pointers are internal to it so it's 30 # easier to keep in a consistent state. And it can now be passed directly to 31 # helpers like 'read-byte' or 'read-until' that only need to support streams, 32 # never file descriptors. 33 # 34 # Like with 'write', we assume our data segment will never begin at an address 35 # shorter than 0x08000000, so any smaller arguments are assumed to be real 36 # file descriptors. 37 # 38 # As a reminder, a stream looks like this: 39 # write: int # index at which to write to next 40 # read: int # index at which to read next 41 # data: (array byte) # prefixed by length as usual 42 43 == code 44 # instruction effective address register displacement immediate 45 # . op subop mod rm32 base index scale r32 46 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes 47 48 read: # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX 49 # . prolog 50 55/push-EBP 51 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 52 # if (f < 0x08000000) return _read(f, s) # f can't be a user-mode address, so treat it as a kernel file descriptor 53 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) 54 73/jump-if-greater-unsigned-or-equal $read:fake/disp8 55 # . . push args 56 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 57 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 58 # . . call 59 e8/call _read/disp32 60 # . . discard args 61 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 62 # return 63 eb/jump $read:end/disp8 64 $read:fake: 65 # otherwise, treat 'f' as a stream to scan from 66 # . save registers 67 56/push-ESI 68 57/push-EDI 69 # ESI = f 70 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI 71 # EDI = s 72 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to ESI 73 # EAX = _buffer-4(out = &s->data[s->write], outend = &s->data[s->length], 74 # in = &f->data[f->read], inend = &f->data[f->write]) 75 # . . push &f->data[f->write] 76 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX 77 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX 78 50/push-EAX 79 # . . push &f->data[f->read] 80 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX 81 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX 82 50/push-EAX 83 # . . push &s->data[s->length] 84 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 8/disp8 . # copy *(EDI+8) to EAX 85 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX 86 50/push-EAX 87 # . . push &s->data[s->write] 88 8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX 89 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX 90 50/push-EAX 91 # . . call 92 e8/call _buffer-4/disp32 93 # . . discard args 94 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP 95 # s->write += EAX 96 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI 97 # f->read += EAX 98 01/add 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # add EAX to *(ESI+4) 99 # . restore registers 100 5f/pop-to-EDI 101 5e/pop-to-ESI 102 $read:end: 103 # . epilog 104 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP 105 5d/pop-to-EBP 106 c3/return 107 108 # - helpers 109 110 # '_buffer' is like '_append', but silently stops instead of aborting when it runs out of space 111 112 # 3-argument variant of _buffer 113 _buffer-3: # out : address, outend : address, s : (array byte) -> num_bytes_buffered/EAX 114 # . prolog 115 55/push-EBP 116 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 117 # . save registers 118 51/push-ECX 119 # EAX = _buffer-4(out, outend, &s->data[0], &s->data[s->length]) 120 # . . push &s->data[s->length] 121 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX 122 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX 123 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX 124 51/push-ECX 125 # . . push &s->data[0] 126 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy EAX+4 to ECX 127 51/push-ECX 128 # . . push outend 129 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 130 # . . push out 131 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 132 # . . call 133 e8/call _buffer-4/disp32 134 # . . discard args 135 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP 136 $_buffer-3:end: 137 # . restore registers 138 59/pop-to-ECX 139 # . epilog 140 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP 141 5d/pop-to-EBP 142 c3/return 143 144 # 4-argument variant of _buffer 145 _buffer-4: # out : address, outend : address, in : address, inend : address -> num_bytes_buffered/EAX 146 # . prolog 147 55/push-EBP 148 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 149 # . save registers 150 51/push-ECX 151 52/push-EDX 152 53/push-EBX 153 56/push-ESI 154 57/push-EDI 155 # EAX/num_bytes_buffered = 0 156 b8/copy-to-EAX 0/imm32 157 # EDI = out 158 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI 159 # EDX = outend 160 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX 161 # ESI = in 162 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0x10/disp8 . # copy *(EBP+16) to ESI 163 # ECX = inend 164 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x14/disp8 . # copy *(EBP+20) to ECX 165 $_buffer-4:loop: 166 # if (in >= inend) break 167 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX 168 7d/jump-if-greater-or-equal $_buffer-4:end/disp8 169 # if (out >= outend) break # for now silently ignore filled up buffer 170 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX 171 7d/jump-if-greater-or-equal $_buffer-4:end/disp8 172 # *out = *in 173 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL 174 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI 175 # ++num_bytes_buffered 176 40/increment-EAX 177 # ++in 178 46/increment-ESI 179 # ++out 180 47/increment-EDI 181 eb/jump $_buffer-4:loop/disp8 182 $_buffer-4:end: 183 # . restore registers 184 5f/pop-to-EDI 185 5e/pop-to-ESI 186 5b/pop-to-EBX 187 5a/pop-to-EDX 188 59/pop-to-ECX 189 # . epilog 190 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP 191 5d/pop-to-EBP 192 c3/return 193 194 195 # idea: a clear-if-empty method on streams that clears only if f->read == f->write 196 # Unclear how I'd use it, though. Callers seem to need the check anyway. 197 # Maybe a better helper would be 'empty-stream?' 198 199 _read: # fd : int, s : (address stream) -> num-bytes-read/EAX 200 # . prolog 201 55/push-EBP 202 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 203 # . save registers 204 51/push-ECX 205 52/push-EDX 206 53/push-EBX 207 56/push-ESI 208 # ESI = s 209 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI 210 # EAX = s->write 211 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX 212 # EDX = s->length 213 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 8/disp8 . # copy *(ESI+8) to EDX 214 # syscall(read, fd, &s->data[s->write], s->length - s->write) 215 # . . fd : EBX 216 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX 217 # . . data : ECX = &s->data[s->write] 218 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX 0xc/disp8 . # copy ESI+EAX+12 to ECX 219 # . . size : EDX = s->length - s->write 220 29/subtract 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # subtract EAX from EDX 221 # . . syscall 222 b8/copy-to-EAX 3/imm32/read 223 cd/syscall 0x80/imm8 224 # add the result EAX to s->write 225 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI 226 $_read:end: 227 # . restore registers 228 5e/pop-to-ESI 229 5b/pop-to-EBX 230 5a/pop-to-EDX 231 59/pop-to-ECX 232 # . epilog pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Mu - subx/066write-buffered.subx</title> <meta name="Generator" content="Vim/8.0"> <meta name="plugin-version" content="vim7.4_v2"> <meta name="syntax" content="none"> <meta name="settings" content="number_lines,use_css,no_foldcolumn,expand_tabs,line_ids,prevent_copy="> <meta name="colorscheme" content="minimal-light"> <style type="text/css"> <!-- pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } .Constant { color: #008787; } .LineNr { } .subxS1Comment { color: #0000af; } .CommentedCode { color: #8a8a8a; } .subxFunction { color: #af5f00; text-decoration: underline; } .subxTest { color: #5f8700; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } .subxH1Comment { color: #005faf; text-decoration: underline; } --> </style> <script type='text/javascript'> <!-- /* function to open any folds containing a jumped-to line before jumping to it */ function JumpToLine() { var lineNum; lineNum = window.location.hash; lineNum = lineNum.substr(1); /* strip off '#' */ if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ if (lineElem) { lineElem.scrollIntoView(true); } return true; } if ('onhashchange' in window) { window.onhashchange = JumpToLine; } --> </script> </head> <body onload='JumpToLine();'> <a href='https://github.com/akkartik/mu/blob/master/subx/066write-buffered.subx'>https://github.com/akkartik/mu/blob/master/subx/066write-buffered.subx</a> <pre id='vimCodeElement'> <span id="L1" class="LineNr"> 1 </span><span class="subxComment"># write-buffered: like 'write', but for a buffered-file</span> <span id="L2" class="LineNr"> 2 </span> <span id="L3" class="LineNr"> 3 </span>== code <span id="L4" class="LineNr"> 4 </span><span class="subxComment"># instruction effective address register displacement immediate</span> <span id="L5" class="LineNr"> 5 </span><span class="subxS1Comment"># . op subop mod rm32 base index scale r32</span> <span id="L6" class="LineNr"> 6 </span><span class="subxS1Comment"># . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes</span> <span id="L7" class="LineNr"> 7 </span> <span id="L8" class="LineNr"> 8 </span><span class="CommentedCode">#? Entry: # run a single test, while debugging</span> <span id="L9" class="LineNr"> 9 </span><span class="CommentedCode">#? e8/call test-write-buffered/disp32</span> <span id="L10" class="LineNr"> 10 </span><span class="CommentedCode">#? e8/call test-write-buffered-with-intermediate-flush/disp32</span> <span id="L11" class="LineNr"> 11 </span><span class="CommentedCode">#? # syscall(exit, Num-test-failures)</span> <span id="L12" class="LineNr"> 12 </span><span class="CommentedCode">#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX</span> <span id="L13" class="LineNr"> 13 </span><span class="CommentedCode">#? b8/copy-to-EAX 1/imm32/exit</span> <span id=