https://github.com/akkartik/mu/blob/master/064write-byte.subx
  1 # write-byte-buffered: add a single byte to a buffered-file.
  2 # flush: write out any buffered writes to disk.
  3 #
  4 # TODO: Come up with a way to signal failure to write to disk. This is hard
  5 # since the failure may impact previous calls that were buffered.
  6 
  7 == data
  8 
  9 # The buffered file for standard output.
 10 Stdout:
 11     # file descriptor or (address stream)
 12     1/imm32  # standard output
 13     # current write index
 14     0/imm32
 15     # current read index
 16     0/imm32
 17     # length
 18     8/imm32
 19     # data
 20     00 00 00 00 00 00 00 00  # 8 bytes
 21 
 22 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
 23 # I don't want to type in 1024 bytes here.
 24 
 25 == code
 26 #   instruction                     effective address                                                   register    displacement    immediate
 27 # . op          subop               mod             rm32          base        index         scale       r32
 28 # . 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
 29 
 30 # Write lower byte of 'n' to 'f'.
 31 write-byte-buffered:  # f : (address buffered-file), n : int -> <void>
 32     # . prolog
 33     55/push-ebp
 34     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 35     # . save registers
 36     51/push-ecx
 37     57/push-edi
 38     # edi = f
 39     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
 40     # ecx = f->write
 41     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 42     # if (f->write >= f->length) flush and clear f's stream
 43     3b/compare                      1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   0xc/disp8       .                 # compare ecx with *(edi+12)
 44     7c/jump-if-lesser  $write-byte-buffered:to-stream/disp8
 45     # . flush(f)
 46     # . . push args
 47     57/push-edi
 48     # . . call
 49     e8/call  flush/disp32
 50     # . . discard args
 51     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           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 */
# -*- encoding: utf8 -*-
# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License

import os.path
import sys
rangerpath = os.path.join(os.path.dirname(__file__), '..')
if sys.path[1] != rangerpath:
	sys.path[1:1] = [rangerpath]
sys.path[1:1] = ['..']

from unittest import TestCase, main
from ranger.ext.utfwidth import *

a_ascii = "a"      # width = 1, bytes = 1
a_umlaut = "ä"     # width = 1, bytes = 2
a_katakana = "ア"  # width = 2, bytes = 3
# need one with width = 1 & bytes = 3

class Test(TestCase):
	def test_utf_byte_length(self):
		self.assertEqual(1, utf_byte_length(a_ascii))
		self.assertEqual(2, utf_byte_length(a_umlaut))
		self.assertEqual(3, utf_byte_length(a_katakana))

	def test_uwid(self):
		self.assertEqual(1, uwid(a_ascii))
		self.assertEqual(1, uwid(a_umlaut))
		self.assertEqual(2, uwid(a_katakana))
		self.assertEqual(3, uwid(a_katakana + a_umlaut))
		self.assertEqual(4, uwid("asdf"))
		self.assertEqual(5, uwid("löööl"))
		self.assertEqual(6, uwid("バババ"))

if __name__ == '__main__': main()
. 8/imm32 # add to esp 133 # flush(_test-buffered-file) 134 # . . push args 135 68/push _test-buffered-file/imm32 136 # . . call 137 e8/call flush/disp32 138 # . . discard args 139 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 140 # check-stream-equal(_test-stream, "A", msg) 141 # . . push args 142 68/push "F - test-write-byte-buffered-single"/imm32 143 68/push "A"/imm32 144 68/push _test-stream/imm32 145 # . . call 146 e8/call check-stream-equal/disp32 147 # . . discard args 148 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 149 # . end 150 c3/return 151 152 test-write-byte-buffered-multiple-flushes: 153 # - check that write-byte-buffered correctly flushes buffered data 154 # setup 155 # . clear-stream(_test-stream) 156 # . . push args 157 68/push _test-stream/imm32 158 # . . call 159 e8/call clear-stream/disp32 160 # . . discard args 161 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 162 # . clear-stream(_test-buffered-file+4) 163 # . . push args 164 b8/copy-to-eax _test-buffered-file/imm32 165 05/add-to-eax 4/imm32 166 50/push-eax 167 # . . call 168 e8/call clear-stream/disp32 169 # . . discard args 170 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 171 # fill up the buffer for _test-buffered-file 172 # . write(_test-buffered-file+4, "abcdef") 173 # . . push args 174 68/push "abcdef"/imm32 175 b8/copy-to-eax _test-buffered-file/imm32 176 05/add-to-eax 4/imm32 177 50/push-eax 178 # . . call 179 e8/call write/disp32 180 # . . discard args 181 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 182 # write-byte-buffered(_test-buffered-file, 'g') 183 # . . push args 184 68/push 0x67/imm32 185 68/push _test-buffered-file/imm32 186 # . . call 187 e8/call write-byte-buffered/disp32 188 # . . discard args 189 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 190 # flush(_test-buffered-file) 191 # . . push args 192 68/push _test-buffered-file/imm32 193 # . . call 194 e8/call flush/disp32 195 # . . discard args 196 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 197 # check-stream-equal(_test-stream, "abcdefg", msg) 198 # . . push args 199 68/push "F - test-write-byte-buffered-multiple-flushes"/imm32 200 68/push "abcdefg"/imm32 201 68/push _test-stream/imm32 202 # . . call 203 e8/call check-stream-equal/disp32 204 # . . discard args 205 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 206 # . end 207 c3/return 208 209 # - variant without buffering 210 211 # Write lower byte of 'n' to 'f'. 212 append-byte: # f : (address stream), n : int -> <void> 213 # . prolog 214 55/push-ebp 215 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 216 # . save registers 217 51/push-ecx 218 57/push-edi 219 # edi = f 220 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi 221 # ecx = f->write 222 8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx 223 # if (f->write >= f->length) abort 224 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 8/disp8 . # compare ecx with *(edi+8) 225 7d/jump-if-greater-or-equal $append-byte:abort/disp8 226 $append-byte:to-stream: 227 # write to stream 228 # f->data[f->write] = LSB(n) 229 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 230 8a/copy-byte 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/AL 0xc/disp8 . # copy byte at *(ebp+12) to AL 231 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+ecx+12) 232 # ++f->write 233 ff 0/subop/increment 0/mod/indirect 7/rm32/edi . . . . . . # increment *edi 234 $append-byte:end: 235 # . restore registers 236 5f/pop-to-edi 237 59/pop-to-ecx 238 # . epilog 239 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 240 5d/pop-to-ebp 241 c3/return 242 243 $append-byte:abort: 244 # . _write(2/stderr, error) 245 # . . push args 246 68/push "append-byte: out of space\n"/imm32 247 68/push 2/imm32/stderr 248 # . . call 249 e8/call _write/disp32 250 # . . discard args 251 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 252 # . syscall(exit, 1) 253 bb/copy-to-ebx 1/imm32 254 b8/copy-to-eax 1/imm32/exit 255 cd/syscall 0x80/imm8 256 # never gets here 257 258 test-append-byte-single: 259 # - check that append-byte writes to first byte of 'file' 260 # setup 261 # . clear-stream(_test-stream) 262 # . . push args 263 68/push _test-stream/imm32 264 # . . call 265 e8/call clear-stream/disp32 266 # . . discard args 267 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 268 # append-byte(_test-stream, 'A') 269 # . . push args 270 68/push 0x41/imm32 271 68/push _test-stream/imm32 272 # . . call 273 e8/call append-byte/disp32 274 # . . discard args 275 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 276 # check-stream-equal(_test-stream, "A", msg) 277 # . . push args 278 68/push "F - test-append-byte-single"/imm32 279 68/push "A"/imm32 280 68/push _test-stream/imm32 281 # . . call 282 e8/call check-stream-equal/disp32 283 # . . discard args 284 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 285 # . end 286 c3/return 287 288 == data 289 290 _test-output-stream: 291 # current write index 292 0/imm32 293 # current read index 294 0/imm32 295 # length 296 0x200/imm32 # 512 bytes 297 # data (32 lines x 16 bytes/line) 298 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 299 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 301 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 302 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 303 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 304 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 305 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 306 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 307 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 308 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 309 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 311 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 312 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 313 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 314 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 315 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 316 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 317 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 318 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 319 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 321 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 322 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 323 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 324 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 325 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 326 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 327 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 328 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 329 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 330 331 # a test buffered file for _test-output-stream 332 _test-output-buffered-file: 333 # file descriptor or (address stream) 334 _test-output-stream/imm32 335 # current write index 336 0/imm32 337 # current read index 338 0/imm32 339 # length 340 6/imm32 341 # data 342 00 00 00 00 00 00 # 6 bytes 343 344 _test-error-stream: 345 # current write index 346 0/imm32 347 # current read index 348 0/imm32 349 # line 350 0x80/imm32 # 128 bytes 351 # data (8 lines x 16 bytes/line) 352 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 353 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 354 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 355 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 356 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 357 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 358 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 359 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 360 361 # a test buffered file for _test-error-stream 362 _test-error-buffered-file: 363 # file descriptor or (address stream) 364 _test-error-stream/imm32 365 # current write index 366 0/imm32 367 # current read index 368 0/imm32 369 # length 370 6/imm32 371 # data 372 00 00 00 00 00 00 # 6 bytes 373 374 # . . vim:nowrap:textwidth=0