emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/beardbolt 291f82a324 266/323: Continue rewriting


From: ELPA Syncer
Subject: [elpa] externals/beardbolt 291f82a324 266/323: Continue rewriting
Date: Thu, 9 Mar 2023 10:58:37 -0500 (EST)

branch: externals/beardbolt
commit 291f82a324a1e02853e9ae8a1050bf4ca9997579
Author: João Távora <joaotavora@gmail.com>
Commit: João Távora <joaotavora@gmail.com>

    Continue rewriting
---
 beardbolt.el                 |  930 ++++++++++++++++-------------------
 starters/slow-to-process.cpp | 1099 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1506 insertions(+), 523 deletions(-)

diff --git a/beardbolt.el b/beardbolt.el
index 57bdb7e1af..6254097bf3 100644
--- a/beardbolt.el
+++ b/beardbolt.el
@@ -25,45 +25,7 @@
 ;; beardbolt is a fork of the amazing rmsbolt.el, found at
 ;; https://gitlab.com/jgkamat/rmsbolt, a package to provide assembly or
 ;; bytecode output for a source code input file.
-;;
-;; It currently supports: C/C++, OCaml, Haskell, Python, Java, Go, PHP, D,
-;; Pony, Zig, Swift, Emacs Lisp, and (limited) Common Lisp.
-;;
-;; Adding support for more languages, if they have an easy manual compilation
-;; path from source->assembly/bytecode with debug information, should be much
-;; easier than in other alternatives.
-;;
-;; It's able to do this by:
-;; 1. Compiling changes automatically, adding options which cause the compiler
-;; to output assembly/bytecode with debug information (or by using objdump)
-;; 2. Parse assembly/bytecode to create a map from it to the original source
-;; 3. Strip out unneeded information from bytecode to only show useful code
-;; 4. Provide an interface for highlighting the matched assembly/bytecode line
-;; to the source and vice versa
-;;
-;; Tweakables:
-;; beardbolt is primarily configured with Emacs local variables. This lets you
-;; change compiler and beardbolt options simply by editing a local variable 
block.
-;;
-;; Notable options:
-;; `beardbolt-command': prefix of the compilation command to use.
-;; `beardbolt-default-directory': default-drectory to compile from.
-;; `beardbolt-disassemble': disassemble from compiled binary with objdump
-;; `beardbolt-filter-*': Tweak filtering of binary output.
-;; `beardbolt-asm-format': Choose between intel att, and other syntax if 
supported.
-;; `beardbolt-demangle': Demangle the output, if supported.
-;;
-;; For more advanced configuration (to the point where you can override almost
-;; all of beardbolt yourself), you can set `beardbolt-language-descriptor' 
with a
-;; replacement language spec.
-;;
-;; Please see the readme at https://gitlab.com/jgkamat/beardbolt for
-;; more information!
-;;
-;; Thanks:
-;; Inspiration and some assembly parsing logic was adapted from Matt Godbolt's
-;; compiler-explorer: https://github.com/mattgodbolt/compiler-explorer and
-;; Jonas Konrad's javap: https://github.com/yawkat/javap.
+
 
 ;;; Requires:
 
@@ -91,7 +53,7 @@
   "The base command to run beardbolt from."
   :type 'string
   ;; nil means use default command
-  :safe (lambda (v) (or (booleanp v) (stringp v)))
+  :safe (lambda (v) (or (booleanp v) (listp v) (stringp v)))
   :group 'beardbolt)
 
 (defcustom bb-asm-format 'att
@@ -106,17 +68,22 @@ If you are not on x86, you most likely want to set this to 
nil."
   :type 'string
   :safe (lambda (v) (or (booleanp v) (symbolp v) (stringp v)))
   :group 'beardbolt)
-(defcustom bb-filter-directives t
-  "Whether to filter assembly directives."
+(defcustom bb-preserve-directives nil
+  "Whether to preserve assembly directives."
+  :type 'boolean
+  :safe 'booleanp
+  :group 'beardbolt)
+(defcustom bb-preserve-labels nil
+  "Whether to preserve unused labels."
   :type 'boolean
   :safe 'booleanp
   :group 'beardbolt)
-(defcustom bb-filter-labels t
-  "Whether to filter unused labels."
+(defcustom bb-preserve-library-functions t
+  "Whether to preserve library function."
   :type 'boolean
   :safe 'booleanp
   :group 'beardbolt)
-(defcustom bb-filter-comment-only t
+(defcustom bb-preserve-comments nil
   "Whether to filter comment-only lines."
   :type 'boolean
   :safe 'booleanp
@@ -131,18 +98,6 @@ If you are not on x86, you most likely want to set this to 
nil."
   :type 'boolean
   :safe 'booleanp
   :group 'beardbolt)
-(defcustom bb-flag-quirks t
-  "Whether to tweak flags to enable as many features as possible.
-
-In most cases, we will try to honor flags in bb-command as
-much as possible. However, some features may be disabled with
-some odd combinations of flags. This variable controls
-removing/adding flags to handle those cases.
-
-Note that basic flags to ensure basic usage are always modified."
-  :type 'boolean
-  :safe 'booleanp
-  :group 'beardbolt)
 
 ;;;; Faces
 
@@ -154,8 +109,10 @@ Note that basic flags to ensure basic usage are always 
modified."
 ;;;; Basic model
 (defvar-local bb--output-buffer nil)
 (defvar-local bb--source-buffer nil)
+(defvar-local bb--compile-spec nil)
+(defvar-local bb--declared-output nil)
 (defvar-local bb--dump-file nil "Temporary file")
-(defvar-local bb-line-mappings nil "Maps source lines -> asm regions")
+(defvar-local bb--line-mappings nil "Maps source lines -> asm regions")
 (defvar-local bb--relation-overlays nil "Overlays relating source to asm.")
 (defvar-local bb--rainbow-overlays nil "Rainbow overlays.")
 
@@ -166,17 +123,11 @@ Note that basic flags to ensure basic usage are always 
modified."
         (setq bb--output-buffer
               (with-current-buffer
                   (generate-new-buffer (format "*bb-output for %s*" 
src-buffer))
-                (asm-mode)
-                (setq bb--source-buffer src-buffer)
-                (bb--output-mode)
                 (current-buffer))))))
 
-;; whether bb-mode is enabled.
 (defvar bb-hide-compile t)
 
-(defvar bb-binary-asm-limit 10000)
-
-(defvar bb-compile-delay 0.4
+(defvar bb-compile-delay 1.0
   "Time in seconds to delay before recompiling if there is a change.")
 
 (defvar bb--shell "bash"
@@ -187,120 +138,92 @@ Used to work around inconsistencies in alternative 
shells.")
   "Temporary directory to use for compilation and other reasons.")
 
 (defun bb--temp-dir ()
-  (or (and bb--temp-dir
-           (file-exists-p bb--temp-dir)
-           bb--temp-dir)
+  (or (and bb--temp-dir (file-exists-p bb--temp-dir) bb--temp-dir)
       (setq bb--temp-dir (make-temp-file "beardbolt-bb-" t)))) 
 
-(defvar bb-dir nil
+(defvar bb-dir (file-name-directory load-file-name)
   "The directory which beardbolt is installed to.")
-(when load-file-name
-  (setq bb-dir (file-name-directory load-file-name)))
 
 (defvar-local bb-objdump-binary "objdump"
   "A binary to use for objdumping when using `bb-disassemble'.
 Useful if you have multiple objdumpers and want to select between them")
 
-;;;; Variable-like funcs
-(defun bb-output-filename (src-buffer &optional asm)
-  "Function for generating an output filename for SRC-BUFFER.
+;;;; Regexes
 
-Outputs assembly file if ASM.
-This function does NOT quote the return value for use in inferior shells."
-  (if (and (not asm)
-           (buffer-local-value 'bb-disassemble src-buffer))
-      (expand-file-name "beardbolt.out" (bb--temp-dir))
-    (expand-file-name "beardbolt.s" (bb--temp-dir))))
+(defvar bb-label-start  "^\\([^:]+\\): *\\(?:#\\|$\\)\\(?:.*\\)")
 
-;;;; Regexes
 
-(defvar bb-label-def  (rx bol (group (any ".a-zA-Z_$@")
-                                          (0+ (any "a-zA-Z0-9$_@.")))
-                               ":"))
 (defvar bb-defines-global (rx bol (0+ space) ".glob"
-                                   (opt "a") "l" (0+ space)
-                                   (group (any ".a-zA-Z_")
-                                          (0+ (any "a-zA-Z0-9$_.")))))
-(defvar bb-label-find (rx (any ".a-zA-Z_")
+                              (opt "a") "l" (0+ space)
+                              (group (any ".a-zA-Z_")
+                                     (0+ (any "a-zA-Z0-9$_.")))))
+
+(defvar bb-defines-weak (rx bol (0+ space) ".weak"
+                            (0+ space)
+                            (group (any ".a-zA-Z_")
+                                   (0+ (any "a-zA-Z0-9$_.")))))
+(defvar bb-label-reference (rx (any ".a-zA-Z_")
                                (0+
                                 (any "a-zA-Z0-9$_."))))
-(defvar bb-assignment-def (rx bol (0+ space)
-                                   (group (any ".a-zA-Z_$")
-                                          (1+ (any "a-zA-Z0-9$_.")))
-                                   (0+ space) "="))
-(defvar bb-has-opcode (rx bol (0+ space)
-                               (any "a-zA-Z")))
-
-(defvar bb-defines-function (rx bol (0+ space) ".type"
-                                     (0+ any) "," (0+ space) (any "@%")
-                                     "function" eol))
+(defvar bb-set-directive (rx bol (0+ space) ".set" (1+ space)
+                             (group (any ".a-zA-Z_")
+                                    (0+ (any "a-zA-Z0-9$_.")))
+                             (0+ space) "," (0+ space)
+                             (group (any ".a-zA-Z_")
+                                    (0+ (any "a-zA-Z0-9$_.")))))
+(defvar bb-has-opcode (rx bol (1+ space)
+                          (1+ (any "a-zA-Z"))))
+
+(defvar bb-defines-function-or-object (rx bol
+                                          (0+ space) ".type"
+                                          (0+ space)
+                                          (group (0+ any)) "," (0+ space) (any 
"@%")))
 (defvar bb-data-defn (rx bol (0+ space) "."
-                              (group (or "string" "asciz" "ascii"
-                                         (and
-                                          (optional (any "1248")) "byte")
-                                         "short" "word" "long" "quad" "value" 
"zero"))))
+                         (group (or "string" "asciz" "ascii"
+                                    (and
+                                     (optional (any "1248")) "byte")
+                                    "short" "word" "long" "quad" "value" 
"zero"))))
 
-(defvar bb-directive (rx bol (0+ space) "." (0+ any) eol))
 (defvar bb-endblock (rx "." (or "cfi_endproc" "data" "text" "section")))
 (defvar bb-comment-only (rx bol (0+ space) (or (and (or (any "#@;") "//"))
-                                                    (and "/*" (0+ any) "*/"))
-                                 (0+ any) eol))
+                                               (and "/*" (0+ any) "*/"))
+                            (0+ any) eol))
 (defvar bb-disass-line (rx bol
-                                (group "/" (1+ (not (any ":")))) ":"
-                                (group (1+ num))
-                                (0+ any)))
+                           (group "/" (1+ (not (any ":")))) ":"
+                           (group (1+ num))
+                           (0+ any)))
 (defvar bb-disass-label (rx bol (group (1+ (any digit "a-f")))
-                                 (1+ space) "<"
-                                 (group (1+ (not (any ">")))) ">:" eol))
+                            (1+ space) "<"
+                            (group (1+ (not (any ">")))) ">:" eol))
 (defvar bb-disass-dest (rx (0+ any) (group (1+ (any digit "a-f")))
-                                (1+ space) "<" (group (1+ (not (any ">")))) 
">" eol))
+                           (1+ space) "<" (group (1+ (not (any ">")))) ">" 
eol))
 
 (defvar bb-disass-opcode (rx bol (0+ space) (group (1+ (any digit "a-f")))
-                                  ":" (0+ space)
-                                  (group (1+
-                                          (repeat 2
-                                                  (any digit "a-f"))
-                                          (opt " ")))
-                                  (0+ space)
-                                  (group (0+ any))))
-(defvar bb-source-file (rx bol (0+ space) ".file" (1+ space)
+                             ":" (0+ space)
+                             (group (1+
+                                     (repeat 2
+                                             (any digit "a-f"))
+                                     (opt " ")))
+                             (0+ space)
+                             (group (0+ any))))
+(defvar bb-source-file-hint (rx bol (0+ space) ".file" (1+ space)
                                 (group (1+ digit)) (1+ space) ?\"
                                 (group (1+ (not (any ?\")))) ?\"
                                 (opt (1+ space) ?\"
                                      (group (1+ (not (any ?\")))) ?\")
                                 (0+ any)))
 (defvar bb-source-tag (rx bol (0+ space) ".loc" (1+ space)
-                               (group (1+ digit)) (1+ space)
-                               (group (1+ digit))
-                               (0+ any)))
+                          (group (1+ digit)) (1+ space)
+                          (group (1+ digit))
+                          (0+ any)))
 (defvar bb-source-stab (rx bol (0+ any) ".stabn" (1+ space)
-                                (group (1+ digit)) ",0,"
-                                (group (1+ digit)) "," (0+ any)))
-
-;;;; Classes
+                           (group (1+ digit)) ",0,"
+                           (group (1+ digit)) "," (0+ any)))
 
 (cl-defstruct (bb-lang
                (:constructor make-beardbolt-lang)
                (:conc-name bb--lang-))
-  (objdumper
-   nil :documentation "Object dumper to use if disassembling binary.")
-  (demangler
-   nil :documentation "If non-nil, demangler to use for this source code")
-  (base-cmd
-   nil :documentation "")
-  (compile-cmd-function
-   nil :documentation "")
-  (asm-function
-   nil :documentation "Function to operate on an assembly listing")
-  (disass-function
-   nil :documentation "Function to operate on a binary")
-  (disass-hidden-funcs
-   nil :documentation "Regexp recognizing non-user assembly rountines."))
-
-(defmacro bb--set-local (var val)
-  "Set unquoted variable VAR to value VAL in current buffer."
-  (declare (debug (symbolp form)))
-  `(set (make-local-variable ,var) ,val))
+  (base-cmd nil :documentation "") (compile-specs nil :documentation ""))
 
 (defun bb-split-rm-single (cmd flag &optional test)
   "Remove a single FLAG from CMD.  Test according to TEST."
@@ -318,33 +241,47 @@ This function does NOT quote the return value for use in 
inferior shells."
            concat (and (cl-plusp i) " ")
            and concat probe and do (setq split (cdr split))))
 
-;;;; Language Functions
-;;;;; Compile Commands
-
-(cl-defun bb--c-compile-cmd ()
-  "Process a compile command for gcc/clang."
-  (let* ((cmd (or bb-command
-                  (bb--lang-base-cmd (bb--get-lang))))
-         (cmd (mapconcat #'identity
-                         (list cmd
-                               "-g"
-                               (if bb-disassemble "-c" "-S")
-                               (cond ((derived-mode-p 'c++-mode) "-x c++")
-                                     (t "-x c"))
-                               "-"
-                               "-o" (shell-quote-argument (bb-output-filename
-                                                           (current-buffer)))
-                               (when (and bb-asm-format (not bb-disassemble))
-                                 (format "-masm=%s" bb-asm-format)))
-                         " "))
-         (cmd (if (and bb-flag-quirks
-                       (string-match-p (rx "-save-temps") cmd)
-                       (string-match-p (rx "-P") cmd))
-                  (bb-split-rm-single cmd "-save-temps")
-                cmd)))
-    cmd))
-
-;;;;; Hidden Function Definitions
+(cl-defun bb--c/c++-compile-specs ()
+  "Process a compile command for gcc/clang.
+Returns a list (SPEC ...) where SPEC looks like (WHAT FN CMD)."
+  (cl-labels ((tmp (f newext)
+                (expand-file-name
+                 (format "%s.%s" (file-name-base f) newext) (bb--temp-dir)))
+              (objdump (in)
+                (let ((out (tmp in "bb-objdumped")))
+                  `(("&&" ,bb-objdump-binary "-d" ,in
+                     "--insn-width=16" "-l"
+                     ,(when bb-asm-format
+                        (format "-M %s" bb-asm-format))
+                     ">" ,out)
+                    . ,out)))
+              (join (l &optional (sep " ")) (mapconcat #'identity l sep))
+              (munch (l) (join (mapcar #'join l) " \\\n")))
+    (let* ((direct-asm-out (tmp "beardbolt" "s"))
+           (disass-asm-out (tmp "beardbolt" "out"))
+           (base-command (ensure-list (or bb-command
+                                          (bb--lang-base-cmd (bb--get-lang)))))
+           (debug `("-g"))
+           (stdin-process `("-x" ,(if (derived-mode-p 'c++-mode) "c++" "c") 
"-"))
+           (direct-asm `("-S" ,(format "-masm=%s" bb-asm-format)
+                         "-o" ,direct-asm-out))
+           (disass-asm `("-c" "-o" ,disass-asm-out)))
+      `((:compile
+         ,(lambda (dump-file)
+            (cons
+             (munch `(,base-command ,stdin-process ,debug
+                                    ,direct-asm ("<" ,dump-file)))
+             direct-asm-out))
+         ,#'bb--process-asm)
+        (:compile-assemble-disassemble
+         ,(lambda (dump-file)
+            (let* ((objdump-pair (objdump disass-asm-out)))
+              (cons
+               (munch `(,base-command ,stdin-process ,debug
+                                      ,disass-asm ("<" ,dump-file)
+                                      ,(car objdump-pair)))
+               (cdr objdump-pair))))
+         ,#'bb--process-disassembled-lines)))))
 
 (defvar bb--hidden-func-c
   (rx bol (or (and "__" (0+ any))
@@ -355,242 +292,204 @@ This function does NOT quote the return value for use 
in inferior shells."
               (and ".plt" (0+ any)))
       eol))
 
-;;;; Language Definitions
 (defvar bb-languages
   `((c-mode
-     . ,(make-beardbolt-lang :compile-cmd-function #'bb--c-compile-cmd
-                             :base-cmd "gcc"
-                             :objdumper 'objdump
-                             :asm-function #'bb--process-src-asm-lines
-                             :disass-function #'bb--process-disassembled-lines
-                             :demangler "c++filt"
-                             :disass-hidden-funcs bb--hidden-func-c))
+     . ,(make-beardbolt-lang :compile-specs #'bb--c/c++-compile-specs
+                             :base-cmd "gcc"))
     (c++-mode
-     . ,(make-beardbolt-lang :compile-cmd-function #'bb--c-compile-cmd
-                             :base-cmd "g++"
-                             :objdumper 'objdump
-                             :asm-function #'bb--process-src-asm-lines
-                             :disass-function #'bb--process-disassembled-lines
-                             :demangler "c++filt"
-                             :disass-hidden-funcs bb--hidden-func-c))))
-
-;;;; Macros
+     . ,(make-beardbolt-lang :compile-specs #'bb--c/c++-compile-specs
+                             :base-cmd "g++"))))
 
 (defmacro bb-with-display-buffer-no-window (&rest body)
   "Run BODY without displaying any window."
   ;; See http://debbugs.gnu.org/13594
-  `(let ((display-buffer-overriding-action
-          (if bb-hide-compile
-              (list #'display-buffer-no-window)
-            display-buffer-overriding-action)))
+  `(let ((display-buffer-overriding-action (list #'display-buffer-no-window)))
      ,@body))
 
-;;;; Functions
-;; Functions to parse and lint assembly were lifted almost directly from the 
compiler-explorer
-
-(defun bb-re-seq (regexp string)
-  "Get list of all REGEXP match in STRING."
-  (save-match-data
-    (let ((pos 0)
-          matches)
-      (while (string-match regexp string pos)
-        (push (match-string 0 string) matches)
-        (setq pos (match-end 0)))
-      matches)))
-
-;;;;; Filter Functions
-
-;; Filtering functions were more or less lifted from the godbolt
-;; compiler explorer to maintain compatiblity.
-;; https://github.com/mattgodbolt/compiler-explorer/blob/master/lib/asm.js
-
-(defun bb--has-opcode-p (line)
-  "Check if LINE has opcodes."
-  (save-match-data
-    (let* ((match (string-match bb-label-def line))
-           (line (if match
-                     (substring line (match-end 0))
-                   line))
-           (line (cl-first (split-string line (rx (1+ (any ";#")))))))
-      (if (string-match-p bb-assignment-def line)
-          nil
-        (string-match-p bb-has-opcode line)))))
-
-(defun bb--find-used-labels (src-buffer asm-lines)
-  "Find used labels in ASM-LINES generated from SRC-BUFFER."
-  (let ((match nil)
-        (current-label nil)
-        (labels-used (make-hash-table :test #'equal))
-        (weak-usages (make-hash-table :test #'equal)))
-    (dolist (line asm-lines)
-      (setq line (string-trim-left line)
-            match (and (string-match bb-label-def line)
-                       (match-string 1 line)))
-      (when match
-        (setq current-label match))
-      (setq match (and (string-match bb-defines-global line)
-                       (match-string 1 line)))
-      (when match
-        (puthash match t labels-used))
-      ;; When we have no line or a period started line, skip
-      (unless (or (string-empty-p line)
-                  (eq (elt line 0) ?.)
-                  (not (string-match-p bb-label-find line)))
-        (if (or (not (buffer-local-value 'bb-filter-directives src-buffer))
-                (bb--has-opcode-p line)
-                (string-match-p bb-defines-function line))
-            ;; Add labels indescriminantly
-            (dolist (l (bb-re-seq bb-label-find line))
-              (puthash l t labels-used))
-          (when (and current-label
-                     (or (string-match-p bb-data-defn line)
-                         (bb--has-opcode-p line)))
-            (dolist (l (bb-re-seq bb-label-find line))
-              (cl-pushnew l (gethash current-label weak-usages) :test 
#'equal))))))
-
-    (let* ((max-label-iter 10)
-           (label-iter 0)
-           (completed nil))
-
-      (while (and (<= (cl-incf label-iter)
-                      max-label-iter)
-                  (not completed))
-        (let ((to-add nil))
-          (maphash
-           (lambda (label _v)
-             (dolist (now-used (gethash label weak-usages))
-               (when (not (gethash now-used labels-used))
-                 (cl-pushnew now-used to-add :test #'equal))))
-           labels-used)
-          (if to-add
-              (dolist (l to-add)
-                (puthash l t labels-used))
-            (setq completed t))))
-      labels-used)))
-
-(defun bb--user-func-p (src-buffer func)
-  "Return t if FUNC is a user function.
-Argument SRC-BUFFER source buffer."
-  (let* ((lang (with-current-buffer src-buffer (bb--get-lang)))
-         (regexp (bb--lang-disass-hidden-funcs lang)))
+(defvar bb--demangle-cache (make-hash-table :test #'equal))
+
+(cl-defun bb--demangle-quick (from to)
+  (let* ((s (buffer-substring-no-properties from to))
+         (probe (gethash s bb--demangle-cache)))
+    (when probe
+      (delete-region from to)
+      (goto-char from)
+      (insert probe)
+      t)))
+
+(cl-defun bb--demangle-overlays (ovs)
+  (cl-loop
+   with rep = (lambda (ov r)
+                (with-current-buffer (overlay-buffer ov)
+                  (delete-region (overlay-start ov) (overlay-end ov))
+                  (goto-char (overlay-start ov))
+                  (insert r)
+                  (delete-overlay ov)))
+   for ov in ovs
+   for from = (overlay-start ov) for to = (overlay-end ov)
+   for s = (buffer-substring-no-properties from to)
+   for probe = (gethash s bb--demangle-cache)
+   if probe do (funcall rep ov probe)
+   else collect ov into needy-overlays
+   and collect s into needy-strings
+   and concat (format "%s\n" s) into tosend
+   finally
+   (when needy-strings
+     (with-temp-buffer
+       (save-excursion (insert tosend))
+       (shell-command-on-region (point-min) (point-max) "c++filt" t t)
+       (cl-loop for ov in needy-overlays for s in needy-strings
+                while (re-search-forward "^.*$")
+                do (funcall rep ov (puthash s (match-string 0) 
bb--demangle-cache)))))))
+
+(defun bb--user-func-p (func)
+  "Tell if FUNC is user's."
+  (let* ((regexp bb--hidden-func-c))
     (if regexp (not (string-match-p regexp func)) t)))
 
-;; TODO godbolt does not handle disassembly with filter=off, but we should.
-(cl-defun bb--process-disassembled-lines (src-buffer asm-lines)
-  "Process and filter disassembled ASM-LINES from SRC-BUFFER."
-  (let* ((src-file-name "<stdin>")
-         (result nil)
-         (func nil)
-         (source-linum nil))
-    (dolist (line asm-lines)
-      (catch 'continue
-        (when (and (> (length result) bb-binary-asm-limit)
-                   (not (buffer-local-value 'bb-ignore-binary-limit 
src-buffer)))
-          (cl-return-from bb--process-disassembled-lines
-            '("Aborting processing due to exceeding the binary limit.")))
-        (when (string-match bb-disass-line line)
-          ;; Don't add linums from files which we aren't inspecting
-          (if (equal src-file-name
-                     (file-name-base (match-string 1 line)))
-              (setq source-linum (string-to-number (match-string 2 line)))
-            (setq source-linum nil))
-          ;; We are just setting a linum, no data here.
-          (throw 'continue t))
-
-        (when (string-match bb-disass-label line)
-          (setq func (match-string 2 line))
-          (when (bb--user-func-p src-buffer func)
-            (push (concat func ":") result))
-          (throw 'continue t))
-        (unless (and func
-                     (bb--user-func-p src-buffer func))
-          (throw 'continue t))
-        (when (string-match bb-disass-opcode line)
-          (let ((line (concat (match-string 1 line)
-                              "\t" (match-string 3 line))))
-            ;; Add line text property if available
-            (when source-linum
-              (add-text-properties 0 (length line)
-                                   `(bb-src-line ,source-linum) line))
-            (push line result))
-          (throw 'continue t))))
-    (nreverse result)))
-
-(cl-defun bb--process-src-asm-lines (src-buffer asm-lines)
-  (let* ((used-labels (bb--find-used-labels src-buffer asm-lines))
-         (src-file-name "<stdin>")
-         (result nil)
-         (prev-label nil)
-         (source-linum nil)
-         (source-file-map (make-hash-table :test #'eq)))
-    (dolist (line asm-lines)
-      (let* ((raw-match (or (string-match bb-label-def line)
-                            (string-match bb-assignment-def line)))
-             (match (when raw-match
-                      (match-string 1 line)))
-             (used-label-p (gethash match used-labels)))
-        (catch 'continue
-          (cond
-           ;; Process file name hints
-           ((string-match bb-source-file line)
-            (if (match-string 3 line)
-                ;; Clang style match
-                (puthash (string-to-number (match-string 1 line))
-                         (expand-file-name (match-string 3 line) (match-string 
2 line))
-                         source-file-map)
-              (puthash (string-to-number (match-string 1 line))
-                       (match-string 2 line)
-                       source-file-map)))
-           ;; Process any line number hints
-           ((string-match bb-source-tag line)
-            (if (equal src-file-name
-                       (gethash
-                        (string-to-number (match-string 1 line))
-                        source-file-map
-                        ;; Assume we never will compile dev null :P
-                        "/dev/null"))
-                (setq source-linum (string-to-number
-                                    (match-string 2 line)))
-              (setq source-linum nil)))
-           ((string-match bb-source-stab line)
-            (pcase (string-to-number (match-string 1 line))
-              ;; http://www.math.utah.edu/docs/info/stabs_11.html
-              (68
-               (setq source-linum (match-string 2 line)))
-              ((or 100 132)
-               (setq source-linum nil)))))
-          ;; End block, reset prev-label and source
-          (when (string-match-p bb-endblock line)
-            (setq prev-label nil))
-
-          (when (and (buffer-local-value 'bb-filter-comment-only src-buffer)
-                     (string-match-p bb-comment-only line))
-            (throw 'continue t))
-
-          ;; continue means we don't add to the ouptut
-          (when match
-            (if (not used-label-p)
-                ;; Unused label
-                (when (buffer-local-value 'bb-filter-labels src-buffer)
-                  (throw 'continue t))
-              ;; Real label, set prev-label
-              (setq prev-label raw-match)))
-          (when (and (buffer-local-value 'bb-filter-directives src-buffer)
-                     (not match))
-            (if  (and (string-match-p bb-data-defn line)
-                      prev-label)
-                ;; data is being used
-                nil
-              (when (string-match-p bb-directive line)
-                (throw 'continue t))))
-          ;; Add line numbers to mapping
-          (when (and source-linum
-                     (bb--has-opcode-p line))
-            (add-text-properties 0 (length line)
-                                 `(bb-src-line ,source-linum) line))
-          ;; Add line
-          (push line result))))
-    (nreverse result)))
+(defmacro bb--sweeping (&rest forms)
+  (declare (indent 0)
+           (debug (&rest (form &rest form))))
+  (let ((lbp (cl-gensym "lbp-")) (lep (cl-gensym "lep-"))
+        (preserve-directives (cl-gensym "preserve-directives-")))
+    `(let ((,preserve-directives (buffer-local-value
+                                  'bb-preserve-directives
+                                  bb--source-buffer)))
+       (goto-char (point-min))
+       (while (not (eobp))
+         (let ((,lbp (line-beginning-position)) (,lep (line-end-position)))
+           (cl-macrolet ((match (&rest res)
+                           `(cl-loop for re in ,(cons 'list res)
+                                     thereis (re-search-forward re ,',lep t)))
+                         (update-lep () `(setq ,',lep (line-end-position))))
+             (pcase (cond ,@forms)
+               (:preserve (forward-line 1))
+               (:kill (delete-region ,lbp (1+ ,lep)))
+               (_
+                (if ,preserve-directives (forward-line 1)
+                  (delete-region ,lbp (1+ ,lep)))))))))))
+
+(cl-defun bb--process-disassembled-lines ()
+  (let* ((src-file-name "<stdin>") (func nil) (source-linum nil))
+    (bb--sweeping
+      ((match bb-disass-line)
+       (setq source-linum (and (equal src-file-name
+                                      (file-name-base (match-string 1)))
+                               (string-to-number (match-string 2))))
+       :kill)
+      ((match bb-disass-label)
+       (setq func (match-string 2))
+       (when (bb--user-func-p func) (replace-match (concat func ":")))
+       :preserve)
+      ((and func (not (bb--user-func-p func)))
+       :kill)
+      ((match bb-disass-opcode)
+       (when nil
+         (add-text-properties (line-beginning-position) (line-end-position)
+                              `(bb-src-line ,source-linum)))
+       (replace-match (concat (match-string 1) "\t" (match-string 3)))
+       (forward-line 1))
+      (t
+       :kill))))
+
+(cl-defun bb--reachable-p (label globals graph synonyms weaks)
+  (cond ((and (not (buffer-local-value 'bb-preserve-library-functions
+                                       bb--source-buffer))
+              (gethash label weaks))
+         nil)
+        ((gethash label globals) t)
+        (t
+         (maphash (lambda (from to)
+                    (let ((synonym (gethash label synonyms)))
+                      (when (and (or (gethash label to)
+                                     (and synonym (gethash synonym to)))
+                                 (bb--reachable-p from globals graph synonyms 
weaks))
+                        (cl-return-from bb--reachable-p
+                          (progn
+                            (when synonym (puthash synonym t globals))
+                            (puthash label t globals))))))
+                  graph))))
+
+(defun bb--process-asm ()
+  (let ((globals (make-hash-table :test #'equal))
+        (weaks (make-hash-table :test #'equal))
+        (synonyms (make-hash-table :test #'equal))
+        (label-graph (make-hash-table :test #'equal))
+        (src-file-name "<stdin>")
+        (source-file-map (make-hash-table :test #'eq))
+        (source-linum nil)
+        global-label
+        reachable-label
+        demangle-ovs
+        (preserve-comments (buffer-local-value 'bb-preserve-comments 
bb--source-buffer))
+        (preserve-labels (buffer-local-value 'bb-preserve-labels 
bb--source-buffer)))
+    (cl-flet ((schedule-demangling-maybe (from to)
+                (when (and (eq (char-after from) ?_)
+                           (not (bb--demangle-quick from to)))
+                  (let ((ov (make-overlay from to)))
+                    (overlay-put ov 'beardbolt t)
+                    (push ov demangle-ovs)))))
+      ;; first pass
+      (bb--sweeping
+        ((match bb-data-defn) :preserve)
+        ((match bb-label-start)
+         (when (gethash (match-string 1) globals)
+           (setq global-label (match-string 1)))
+         :preserve)
+        ((match bb-source-tag)
+         (setq source-linum
+               (and (equal src-file-name
+                           (gethash
+                            (string-to-number (match-string 1))
+                            source-file-map))
+                    (string-to-number (match-string 2)))))
+        ((match bb-has-opcode)
+         (when source-linum
+           (add-text-properties
+            (match-beginning 0) (match-end 0)
+            (list 'bb-src-line source-linum)))
+         (when global-label
+           (while (match bb-label-reference)
+             (puthash (match-string 0)
+                      t
+                      (or (gethash global-label label-graph)
+                          (puthash global-label (make-hash-table :test #'equal)
+                                   label-graph)))
+             (schedule-demangling-maybe (match-beginning 0) (match-end 0))
+             (update-lep)))
+         :preserve)
+        ((and (not preserve-comments) (match bb-comment-only)) :kill)
+        ((match bb-defines-global bb-defines-function-or-object)
+         (puthash (match-string 1) t globals))
+        ((match bb-defines-weak)
+         (puthash (match-string 1) t weaks))
+        ((match bb-source-file-hint)
+         (puthash (string-to-number (match-string 1))
+                  (or (match-string 3) (match-string 2))
+                  source-file-map))
+        ((match bb-endblock) (setq global-label nil) :preserve)
+        ((match bb-set-directive)
+         (puthash (match-string 2) (match-string 1) synonyms))
+        ((match bb-source-stab)
+         (pcase (string-to-number (match-string 1))
+           ;; http://www.math.utah.edu/docs/info/stabs_11.html
+           (68 (setq source-linum (match-string 2)))
+           ((or 100 132) (setq source-linum nil)))))
+      ;; second pass
+      (setq reachable-label nil)
+      (bb--sweeping
+        ((and (match bb-data-defn bb-has-opcode) reachable-label)
+         :preserve)
+        ((match bb-label-start)
+         (cond
+          ((bb--reachable-p (match-string 1) globals label-graph synonyms 
weaks)
+           (setq reachable-label (match-string 1))
+           (schedule-demangling-maybe (match-beginning 0) (match-end 0))
+           :preserve)
+          (t
+           (if preserve-labels :preserve :kill))))
+        ((match bb-endblock) (setq reachable-label nil)))
+      (bb--demangle-overlays demangle-ovs))))
 
 (defun bb--rainbowize (line-mappings src-buffer)
   (let* ((background-hsl
@@ -634,102 +533,114 @@ Argument SRC-BUFFER source buffer."
                 (overlay-put ov 'beardbolt t)
                 (overlay-put ov 'priority 0)))))))
      line-mappings)
- (mapc #'delete-overlay bb--rainbow-overlays)
+    (mapc #'delete-overlay bb--rainbow-overlays)
     (setq-local bb--rainbow-overlays all-ovs)))
 
+(cl-defmacro bb--when-live-buffer (buf &rest body)
+  "Check BUF live, then do BODY in it." (declare (indent 1) (debug t))
+  (let ((b (cl-gensym)))
+    `(let ((,b ,buf)) (if (buffer-live-p ,b) (with-current-buffer ,b 
,@body)))))
+
 (defun bb--delete-rainbow-overlays ()
+  (bb--when-live-buffer bb--source-buffer
+    (save-restriction
+      (widen)
+      (cl-loop for o in (overlays-in (point-min) (point-max))
+               when (overlay-get o 'beardbolt) do (delete-overlay o))))
   (mapc #'delete-overlay bb--rainbow-overlays)
   (setq bb--rainbow-overlays nil))
 
-(defun bb--make-line-mappings (lines)
+(defun bb--make-line-mappings ()
   (let ((linum 1)
         (start-match nil)
         (in-match nil)
         (ht (make-hash-table)))
-    (dolist (line lines)
-      (let ((property (get-text-property 0 'bb-src-line line)))
-        (progn
-          (cl-tagbody
-           run-conditional
-           (cond
-            ((and in-match (eq in-match property))
-             ;; We are continuing an existing match
-             nil)
-            (in-match
-             ;; We are in a match that has just expired
-             (push (cons start-match (1- linum))
-                   (cl-getf (gethash in-match ht) :lines))
-             (setq in-match nil
-                   start-match nil)
-             (go run-conditional))
-            (property
-             (setq in-match property
-                   start-match linum))))))
-      (cl-incf linum))
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (eobp))
+        (let ((property (get-text-property (point) 'bb-src-line)))
+          (progn
+            (cl-tagbody
+             run-conditional
+             (cond
+              ((and in-match (eq in-match property))
+               ;; We are continuing an existing match
+               nil)
+              (in-match
+               ;; We are in a match that has just expired
+               (push (cons start-match (1- linum))
+                     (cl-getf (gethash in-match ht) :lines))
+               (setq in-match nil
+                     start-match nil)
+               (go run-conditional))
+              (property
+               (setq in-match property
+                     start-match linum))))))
+        (cl-incf linum)
+        (forward-line 1)))
     (maphash (lambda (_k asm-regions)
-             (save-excursion
-               (plist-put
-                asm-regions
-                :positions
-                (cl-loop
-                 for (begl . endl) in (cl-getf asm-regions :lines)
-                 collect (cons (progn
-                                 (goto-char (point-min))
-                                 (forward-line (1- begl))
-                                 (line-beginning-position))
-                               (progn
-                                 (forward-line (- endl begl))
-                                 (line-end-position)))))))
-           ht)
+               (save-excursion
+                 (plist-put
+                  asm-regions
+                  :positions
+                  (cl-loop
+                   for (begl . endl) in (cl-getf asm-regions :lines)
+                   collect (cons (progn
+                                   (goto-char (point-min))
+                                   (forward-line (1- begl))
+                                   (line-beginning-position))
+                                 (progn
+                                   (forward-line (- endl begl))
+                                   (line-end-position)))))))
+             ht)
     ht))
 
 ;;;;; Handlers
-(cl-defun bb--handle-finish-compile (compilation-buffer str &key 
override-buffer)
+(cl-defun bb--handle-finish-compile (compilation-buffer str)
   "Finish hook for compilations.  Runs in buffer COMPILATION-BUFFER.
-Argument STR compilation finish status.
-Argument OVERRIDE-BUFFER asm src buffer to use instead of reading
-   `bb-output-filename'."
+Argument STR compilation finish status."
   (delete-file bb--dump-file)
-  (let ((src-buffer bb--source-buffer))
-    (with-current-buffer (bb--output-buffer src-buffer)
+  (let* ((src-buffer bb--source-buffer)
+         (compile-spec bb--compile-spec)
+         (declared-output bb--declared-output)
+         (output-buffer (bb--output-buffer src-buffer))
+         (split-width-threshold (min split-width-threshold 100)))
+    (with-current-buffer output-buffer
+      (asm-mode)
+      (font-lock-mode -1)
+      (setq bb--source-buffer src-buffer)
+      (bb--output-mode)
+      (buffer-disable-undo)
       ;; Store src buffer value for later linking
       (cond
        ((string-match "^finished" str)
-        (if (and (not override-buffer)
-                 (not (file-exists-p (bb-output-filename src-buffer t))))
-            (message "Error reading from output file.")
-          (let* ((lang (with-current-buffer src-buffer (bb--get-lang)))
-                 (unfiltered-lines
-                  (or (when override-buffer
-                        (with-current-buffer override-buffer
-                          (split-string (buffer-string) "\n" nil)))
-                      (with-temp-buffer
-                        (insert-file-contents (bb-output-filename src-buffer 
t))
-                        (split-string (buffer-string) "\n" nil))))
-                 (lines
-                  (funcall (if (with-current-buffer src-buffer bb-disassemble)
-                               (bb--lang-disass-function lang)
-                             (bb--lang-asm-function lang))
-                           src-buffer unfiltered-lines)))
-            (display-buffer (current-buffer) '(nil (inhibit-same-window . t)))
-            ;; Replace buffer contents but save point and scroll
-            (let* ((window (get-buffer-window))
-                   (old-point (and window (window-point window)))
-                   (old-window-start (and window (window-start window))))
-              (erase-buffer)
-              (insert (mapconcat #'identity lines "\n"))
-              (when window
-                (set-window-start window old-window-start)
-                (set-window-point window old-point)))
-            (setq bb-line-mappings (bb--make-line-mappings lines))
-            (bb--rainbowize bb-line-mappings src-buffer))))
+        (display-buffer (current-buffer) `(() (inhibit-same-window . t)))
+        ;; Replace buffer contents but save point and scroll
+        (let* ((output-window (get-buffer-window))
+               (old-point (and output-window (window-point output-window)))
+               (old-window-start (and output-window (window-start 
output-window))))
+          (erase-buffer)
+          (mapc #'delete-overlay (overlays-in (point-min) (point-max)))
+          (insert-file-contents declared-output)
+          (cond ((eq
+                  t (while-no-input
+                      (save-excursion (funcall (cadr compile-spec)))))
+                 (erase-buffer)
+                 (insert "Interrupted!"))
+                (t
+                 (when output-window
+                   (set-window-start output-window old-window-start)
+                   (set-window-point output-window old-point))
+                 (setq bb--line-mappings (bb--make-line-mappings))
+                 (bb--rainbowize bb--line-mappings src-buffer)
+                 (font-lock-mode 1))))
+        (when-let ((w (get-buffer-window compilation-buffer)))
+          (quit-window nil w)))
        (t
-        ;; Display compilation buffer
-        (display-buffer compilation-buffer '(nil (inhibit-same-window . t)))
-        ;; output no longer up-to-date, kill output buffer
-        ;; immediately.  This also tears down relation overlays
-        ;; in the source buffer, if any.
-        (kill-buffer (current-buffer)))))))
+        (when-let ((w (get-buffer-window)))
+          (quit-window t w))
+        (unless (string-match "^interrupt" str)
+          (display-buffer compilation-buffer '(nil (inhibit-same-window . 
t)))))))))
 
 ;;;;; Parsing Options
 (defvar-local bb--language-descriptor nil)
@@ -738,29 +649,6 @@ Argument OVERRIDE-BUFFER asm src buffer to use instead of 
reading
   (or bb--language-descriptor
       (cdr (assoc major-mode bb-languages))))
 
-(defun bb--demangle-command (existing-cmd lang src-buffer)
-  "Append a demangler routine to EXISTING-CMD with LANG and SRC-BUFFER
-and return it."
-  (if-let ((to-demangle (buffer-local-value 'bb-demangle src-buffer))
-           (demangler (bb--lang-demangler lang))
-           (demangler-exists (executable-find demangler)))
-      (concat existing-cmd " "
-              (mapconcat
-               #'identity
-               (list "&&" demangler
-                     "<" (bb-output-filename src-buffer t)
-                     ">" (expand-file-name "tmp.s" (bb--temp-dir))
-                     "&&" "mv"
-                     (expand-file-name "tmp.s" (bb--temp-dir))
-                     (bb-output-filename src-buffer t))
-               " "))
-    existing-cmd))
-
-(cl-defmacro bb--when-live-buffer (buf &rest body)
-  "Check BUF live, then do BODY in it." (declare (indent 1) (debug t))
-  (let ((b (cl-gensym)))
-    `(let ((,b ,buf)) (if (buffer-live-p ,b) (with-current-buffer ,b 
,@body)))))
-
 (defun bb--compilation-buffer (&rest _)
   (get-buffer-create "*bb-compilation*"))
 
@@ -778,29 +666,17 @@ Interactively, determine LANG from `major-mode'."
                  (error "[beardbolt] Some variables risky %s" risky-vars)))))
     (hack-local-variables))
   (let* ((dump-file
-          (make-temp-file "beardbolt-dump-" nil
-                          (concat "." (file-name-extension buffer-file-name))
-                          (buffer-string)))
+          (let ((inhibit-message t))
+            (make-temp-file "beardbolt-dump-" nil
+                            (concat "." (file-name-extension buffer-file-name))
+                            (buffer-string))))
          (src-buffer (current-buffer))
-         (func (bb--lang-compile-cmd-function lang))
-         (cmd (concat (funcall func) " < " dump-file)))
-    (when bb-disassemble
-      (pcase (bb--lang-objdumper lang)
-        ('objdump
-         (setq cmd
-               (mapconcat #'identity
-                          (list cmd
-                                "&&"
-                                bb-objdump-binary "-d" (bb-output-filename 
src-buffer)
-                                "-C" "--insn-width=16" "-l"
-                                (when bb-asm-format
-                                 (format "-M %s" bb-asm-format))
-                                ">" (bb-output-filename src-buffer t))
-                          " ")))
-        (_
-         (error "Objdumper not recognized"))))
-    ;; Convert to demangle if we need to
-    (setq cmd (bb--demangle-command cmd lang src-buffer))
+         (specs (funcall (bb--lang-compile-specs lang)))
+         (spec (alist-get
+                (if bb-disassemble :compile-assemble-disassemble :compile)
+                specs))
+         (command-and-declared-output (funcall (car spec) dump-file))
+         (cmd (car command-and-declared-output)))
     (with-current-buffer ; With compilation buffer
         (let ((shell-file-name (or (executable-find bb--shell)
                                    shell-file-name))
@@ -810,15 +686,23 @@ Interactively, determine LANG from `major-mode'."
            (compilation-start cmd nil #'bb--compilation-buffer)))
       ;; Only jump to errors, skip over warnings
       (setq-local compilation-skip-threshold 2)
+      (setq-local compilation-always-kill t)
       (setq-local inhibit-message t)
       (add-hook 'compilation-finish-functions
-                #'bb--handle-finish-compile nil t)
+                #'(lambda (&rest whatever)
+                    (let ((inhibit-message nil))
+                      (benchmark-progn
+                        (apply #'bb--handle-finish-compile whatever))))
+                nil t)
       (setq bb--source-buffer src-buffer)
-      (setq bb--dump-file dump-file))))
+      (setq bb--compile-spec spec)
+      (setq bb--dump-file dump-file)
+      (setq bb--declared-output (cdr command-and-declared-output)))))
 
 (defun bb--maybe-stop-running-compilation ()
   (let ((buffer (bb--compilation-buffer)))
     (when-let ((proc (get-buffer-process buffer)))
+      (set-process-query-on-exit-flag proc nil)
       (interrupt-process proc))))
 
 ;;;; Keymap
@@ -906,7 +790,7 @@ Runs in output buffer.  Sets `bb--relation-overlays'."
   (let ((linum (line-number-at-pos nil t)))
     (bb--when-live-buffer bb--output-buffer
       (bb--delete-relation-overlays)
-      (bb--synch-relation-overlays bb-line-mappings linum))))
+      (bb--synch-relation-overlays bb--line-mappings linum))))
 
 (defun bb--on-kill-source-buffer ()
   (bb--when-live-buffer bb--output-buffer
@@ -919,14 +803,14 @@ Runs in output buffer.  Sets `bb--relation-overlays'."
 (defun bb--output-buffer-pch ()
   (bb--delete-relation-overlays)
   (bb--synch-relation-overlays
-   bb-line-mappings
+   bb--line-mappings
    (get-text-property (point) 'bb-src-line)))
 
 (defvar bb--change-timer nil)
 
 (defun bb--after-change (&rest _)
   (bb--when-live-buffer bb--output-buffer
-    (clrhash bb-line-mappings))
+    (when bb--line-mappings (clrhash bb--line-mappings)))
   (when (timerp bb--change-timer) (cancel-timer bb--change-timer))
   (setq bb--change-timer (run-with-timer bb-compile-delay nil 
#'bb--on-change-timer)))
 
diff --git a/starters/slow-to-process.cpp b/starters/slow-to-process.cpp
new file mode 100644
index 0000000000..bb8cfca72e
--- /dev/null
+++ b/starters/slow-to-process.cpp
@@ -0,0 +1,1099 @@
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string>
+#include <strings.h>
+#include <time.h>
+
+#include <algorithm>
+#include <assert.h>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+template <typename T> class Set;
+
+template <typename T> class List : public std::vector<T> {
+  typedef std::vector<T> Base;
+
+public:
+  static const size_t npos = std::string::npos;
+  explicit List(size_t count, T &&defaultValue = T())
+      : Base(count, std::move(defaultValue)) {}
+
+  explicit List() : Base() {}
+
+  template <typename CompatibleType>
+  List(const std::vector<CompatibleType> &other) : Base(other.size(), T()) {
+    const size_t len = other.size();
+    for (size_t i = 0; i < len; ++i) {
+      std::vector<T>::operator[](i) = other.at(i);
+    }
+  }
+
+  template <typename CompatibleType>
+  List(std::vector<CompatibleType> &&other) : Base(other.size(), T()) {
+    const size_t len = other.size();
+    for (size_t i = 0; i < len; ++i) {
+      std::vector<T>::operator[](i) = std::move(other.at(i));
+    }
+  }
+
+  List(std::initializer_list<T> list) : Base(list) {}
+
+  List(typename Base::const_iterator f, typename Base::const_iterator l)
+      : Base(f, l) {}
+
+  bool contains(const T &t) const {
+    return std::find(Base::begin(), Base::end(), t) != Base::end();
+  }
+
+  bool isEmpty() const { return Base::empty(); }
+
+  void append(const T &t) { Base::push_back(t); }
+
+  void prepend(const T &t) { Base::insert(Base::begin(), t); }
+
+  void append(T &&t) { Base::push_back(std::forward<T>(t)); }
+
+  void prepend(T &&t) { Base::insert(Base::begin(), std::forward<T>(t)); }
+
+  void append(const List<T> &t) {
+    const size_t len = t.size();
+    for (size_t i = 0; i < len; ++i)
+      Base::push_back(t.at(i));
+  }
+
+  void insert(size_t idx, const List<T> &list) {
+    Base::insert(Base::begin() + idx, list.begin(), list.end());
+  }
+
+  void insert(size_t idx, const T &val) {
+    Base::insert(Base::begin() + idx, val);
+  }
+
+  void sort() { std::sort(Base::begin(), Base::end()); }
+
+  void sort(std::function<bool(const T &, const T &r)> func) {
+    std::sort(Base::begin(), Base::end(), func);
+  }
+
+  size_t indexOf(const T &t) const {
+    const typename Base::const_iterator beg = Base::begin();
+    const typename Base::const_iterator end = Base::end();
+    const typename Base::const_iterator it = std::find(beg, end, t);
+    return it == end ? npos : (it - beg);
+  }
+
+  size_t lastIndexOf(const T &t, int from = -1) const {
+    const size_t s = size();
+    if (!s)
+      return npos;
+    if (from < 0) {
+      from += s;
+    }
+    from = std::min<int>(s - 1, from);
+    if (from >= 0) {
+      const T *haystack = Base::constData();
+      const T *needle = haystack + from + 1;
+      while (needle != haystack) {
+        if (*--needle == t)
+          return needle - haystack;
+      }
+    }
+    return npos;
+  }
+
+  size_t remove(const T &t) {
+    size_t ret = 0;
+    while (true) {
+      typename Base::iterator it = std::find(Base::begin(), Base::end(), t);
+      if (it == Base::end())
+        break;
+      Base::erase(it);
+      ++ret;
+    }
+    return ret;
+  }
+
+  void removeAt(size_t idx) { Base::erase(Base::begin() + idx); }
+
+  void remove(size_t idx, size_t count) {
+    Base::erase(Base::begin() + idx, Base::begin() + idx + count);
+  }
+
+  void removeLast() { Base::pop_back(); }
+
+  void removeFirst() { Base::erase(Base::begin()); }
+
+  size_t size() const { return Base::size(); }
+
+  T value(size_t idx, const T &defaultValue) const {
+    return idx < Base::size() ? Base::at(idx) : defaultValue;
+  }
+
+  T value(size_t idx) const { return idx < Base::size() ? Base::at(idx) : T(); 
}
+
+  void deleteAll() {
+    typename Base::iterator it = Base::begin();
+    while (it != Base::end()) {
+      delete *it;
+      ++it;
+    }
+    Base::clear();
+  }
+
+  void deleteAll(void (*deleter)(void *t)) {
+    typename Base::iterator it = Base::begin();
+    while (it != Base::end()) {
+      deleter(*it);
+      ++it;
+    }
+    Base::clear();
+  }
+
+  void chop(size_t count) {
+    assert(count <= size());
+    Base::resize(size() - count);
+  }
+
+  size_t truncate(size_t count) {
+    const size_t s = size();
+    if (s > count) {
+      Base::resize(count);
+      return s - count;
+    }
+    return 0;
+  }
+
+  Set<T> toSet() const; // implemented in Set.h
+
+  T &first() { return Base::operator[](0); }
+
+  const T &first() const { return Base::at(0); }
+
+  T takeFirst() {
+    const T ret = first();
+    removeFirst();
+    return ret;
+  }
+
+  T &last() { return Base::operator[](size() - 1); }
+
+  T takeLast() {
+    const T ret = last();
+    removeLast();
+    return ret;
+  }
+
+  const T &last() const { return Base::at(size() - 1); }
+
+  List<T> mid(size_t from, int len = -1) const {
+    assert(from >= 0);
+    const size_t count = Base::size();
+    if (from >= count)
+      return List<T>();
+    if (len < 0) {
+      len = count - from;
+    } else {
+      len = std::min<int>(count - from, len);
+    }
+    return List<T>(Base::begin() + from, Base::begin() + from + len);
+  }
+
+  bool startsWith(const List<T> &t) const {
+    if (size() < t.size())
+      return false;
+    for (size_t i = 0; i < t.size(); ++i) {
+      if (Base::at(i) != t.Base::at(i))
+        return false;
+    }
+    return true;
+  }
+
+  List<T> operator+(const T &t) const {
+    const size_t s = Base::size();
+    List<T> ret(s + 1);
+    for (size_t i = 0; i < s; ++i)
+      ret[i] = Base::at(i);
+    ret[s] = t;
+    return ret;
+  }
+
+  List<T> operator+(const List<T> &t) const {
+    if (t.isEmpty())
+      return *this;
+
+    size_t s = Base::size();
+    List<T> ret(s + t.size());
+
+    for (size_t i = 0; i < s; ++i)
+      ret[i] = Base::at(i);
+
+    for (typename List<T>::const_iterator it = t.begin(); it != t.end(); ++it)
+      ret[s++] = *it;
+
+    return ret;
+  }
+
+  template <typename K> int compare(const List<K> &other) const {
+    const size_t me = size();
+    const size_t him = other.size();
+    if (me < him) {
+      return -1;
+    } else if (me > him) {
+      return 1;
+    }
+    typename List<K>::const_iterator bit = other.begin();
+    for (typename List<T>::const_iterator it = Base::begin(); it != 
Base::end();
+         ++it) {
+      const int cmp = it->compare(*bit);
+      if (cmp)
+        return cmp;
+      ++bit;
+    }
+    return 0;
+  }
+
+  size_t remove(std::function<bool(const T &t)> match) {
+    size_t ret = 0;
+    typename Base::iterator it = Base::begin();
+    while (it != Base::end()) {
+      if (match(*it)) {
+        it = Base::erase(it);
+        ++ret;
+      } else {
+        ++it;
+      }
+    }
+    return ret;
+  }
+
+  typename std::vector<T>::const_iterator constBegin() const {
+    return std::vector<T>::begin();
+  }
+
+  typename std::vector<T>::const_iterator constEnd() const {
+    return std::vector<T>::end();
+  }
+
+  template <typename K> bool operator==(const List<K> &other) const {
+    return !compare(other);
+  }
+
+  template <typename K> bool operator!=(const List<K> &other) const {
+    return compare(other);
+  }
+
+  template <typename K> bool operator<(const List<K> &other) const {
+    return compare(other) < 0;
+  }
+
+  template <typename K> bool operator>(const List<K> &other) const {
+    return compare(other) > 0;
+  }
+
+  List<T> &operator+=(const T &t) {
+    append(t);
+    return *this;
+  }
+
+  List<T> &operator+=(const List<T> &t) {
+    append(t);
+    return *this;
+  }
+
+  List<T> &operator<<(const T &t) {
+    append(t);
+    return *this;
+  }
+
+  List<T> &operator<<(const List<T> &t) {
+    append(t);
+    return *this;
+  }
+};
+
+#define RCT_PRINTF_WARNING(fmt, firstarg)                                      
\
+  __attribute__((__format__(__printf__, fmt, firstarg)))
+
+template <typename T> class String {
+public:
+  static const size_t npos = std::string::npos;
+  typedef size_t size_type;
+
+  enum CaseSensitivity { CaseSensitive, CaseInsensitive };
+  String(const char *ch = 0, size_t len = npos) {
+    if (ch) {
+      if (len == npos)
+        len = strlen(ch);
+      mString.assign(ch, len);
+    }
+  }
+  String(const char *start, const char *end) {
+    if (start) {
+      mString.assign(start, end);
+    }
+  }
+  String(size_t len, char fillChar) : mString(len, fillChar) {}
+
+  String(const String &ba) : mString(ba.mString) {}
+
+  String(String &&ba) : mString(std::move(ba.mString)) {}
+
+  String(const std::string &str) : mString(str) {}
+
+  String(std::string &&str) : mString(std::move(str)) {}
+
+  String &operator=(const String &other) {
+    mString = other.mString;
+    return *this;
+  }
+
+  String &operator=(String &&other) {
+    mString = std::move(other.mString);
+    return *this;
+  }
+
+  void assign(const char *ch, size_t len = npos) {
+    if (ch || !len) {
+      if (len == npos)
+        len = strlen(ch);
+      mString.assign(ch, len);
+    } else {
+      clear();
+    }
+  }
+
+  typedef std::string::const_iterator const_iterator;
+  typedef std::string::iterator iterator;
+
+  const_iterator begin() const { return mString.begin(); }
+  const_iterator end() const { return mString.end(); }
+  iterator begin() { return mString.begin(); }
+  iterator end() { return mString.end(); }
+
+  size_t lastIndexOf(char ch, size_t from = npos,
+                     CaseSensitivity cs = CaseSensitive) const {
+    if (cs == CaseSensitive)
+      return mString.rfind(ch, from == npos ? std::string::npos : 
size_t(from));
+    const char *str = mString.c_str();
+    if (from == npos)
+      from = mString.size() - 1;
+    ch = tolower(ch);
+    int f = static_cast<int>(from);
+    while (f >= 0) {
+      if (tolower(str[f]) == ch)
+        return from;
+      --f;
+    }
+    return npos;
+  }
+
+  size_t indexOf(char ch, size_t from = 0,
+                 CaseSensitivity cs = CaseSensitive) const {
+    if (cs == CaseSensitive)
+      return mString.find(ch, from);
+    const char *data = mString.c_str();
+    ch = tolower(ch);
+    const size_t len = mString.size();
+    while (from < len) {
+      if (tolower(data[from]) == ch)
+        return from;
+      ++from;
+    }
+    return npos;
+  }
+
+  size_t lastIndexOf(const char *ch, size_t len, size_t from = npos,
+                     CaseSensitivity cs = CaseSensitive) const {
+    switch (len) {
+    case 0:
+      return npos;
+    case 1:
+      return lastIndexOf(*ch, from, cs);
+    default: {
+      if (cs == CaseSensitive)
+        return mString.rfind(ch, from, len);
+      if (from == npos)
+        from = mString.size() - 1;
+      String lowered(ch, len);
+      lowered.lowerCase();
+      const size_t needleSize = lowered.size();
+      size_t matched = 0;
+      int f = static_cast<int>(from);
+      while (f >= 0) {
+        if (lowered.at(needleSize - matched - 1) != tolower(at(f))) {
+          matched = 0;
+        } else if (++matched == needleSize) {
+          return f;
+        }
+
+        --f;
+      }
+      break;
+    }
+    }
+    return npos;
+  }
+
+  size_t indexOf(const char *ch, size_t len, size_t from = 0,
+                 CaseSensitivity cs = CaseSensitive) const {
+    switch (len) {
+    case 0:
+      return npos;
+    case 1:
+      return indexOf(*ch, from, cs);
+    default: {
+      if (cs == CaseSensitive)
+        return mString.find(ch, from, len);
+
+      String lowered(ch, len);
+      lowered.lowerCase();
+      const size_t count = size();
+      size_t matched = 0;
+
+      for (size_t i = from; i < count; ++i) {
+        if (lowered.at(matched) != tolower(at(i))) {
+          matched = 0;
+        } else if (++matched == lowered.size()) {
+          return i - matched + 1;
+        }
+      }
+      break;
+    }
+    }
+    return npos;
+  }
+
+  bool contains(const String &other, CaseSensitivity cs = CaseSensitive) const 
{
+    return indexOf(other, 0, cs) != npos;
+  }
+
+  bool contains(char ch, CaseSensitivity cs = CaseSensitive) const {
+    return indexOf(ch, 0, cs) != npos;
+  }
+
+  size_t chomp(const String &chars) {
+    size_t idx = size() - 1;
+    while (idx > 0) {
+      if (chars.contains(at(idx - 1))) {
+        --idx;
+      } else {
+        break;
+      }
+    }
+    const size_t ret = size() - idx - 1;
+    if (ret)
+      resize(idx);
+    return ret;
+  }
+
+  size_t chomp(char ch) { return chomp(String(&ch, 1)); }
+
+  size_t lastIndexOf(const String &ba, size_t from = npos,
+                     CaseSensitivity cs = CaseSensitive) const {
+    return lastIndexOf(ba.constData(), ba.size(), from, cs);
+  }
+
+  size_t indexOf(const String &ba, size_t from = 0,
+                 CaseSensitivity cs = CaseSensitive) const {
+    return indexOf(ba.constData(), ba.size(), from, cs);
+  }
+
+  char first() const { return at(0); }
+
+  char &first() { return operator[](0); }
+
+  char last() const {
+    assert(!isEmpty());
+    return at(size() - 1);
+  }
+
+  char &last() {
+    assert(!isEmpty());
+    return operator[](size() - 1);
+  }
+
+  void lowerCase() {
+    std::transform(mString.begin(), mString.end(), mString.begin(), ::tolower);
+  }
+
+  String toLower() const {
+    std::string ret = mString;
+    std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
+    return ret;
+  }
+
+  String toUpper() const {
+    std::string ret = mString;
+    std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper);
+    return ret;
+  }
+
+  void upperCase() {
+    std::transform(mString.begin(), mString.end(), mString.begin(), ::toupper);
+  }
+
+  String trimmed(const String &trim = " \f\n\r\t\v") const {
+    const size_t start = mString.find_first_not_of(trim);
+    if (start == npos)
+      return String();
+
+    const size_t end = mString.find_last_not_of(trim);
+    assert(end != npos);
+    return mid(start, end - start + 1);
+  }
+
+  enum Pad { Beginning, End };
+  String padded(Pad pad, size_t len, char fillChar = ' ',
+                bool trunc = false) const {
+    const size_t l = length();
+    if (l == len) {
+      return *this;
+    } else if (l > len) {
+      if (!trunc)
+        return *this;
+      if (pad == Beginning) {
+        return right(len);
+      } else {
+        return left(len);
+      }
+    } else {
+      String ret = *this;
+      if (pad == Beginning) {
+        ret.prepend(String(len - l, fillChar));
+      } else {
+        ret.append(String(len - l, fillChar));
+      }
+      return ret;
+    }
+  }
+
+  char *data() { return &mString[0]; }
+
+  void clear() { mString.clear(); }
+  const char *data() const { return mString.data(); }
+  bool isEmpty() const { return mString.empty(); }
+
+  char at(size_t i) const { return mString.at(i); }
+
+  char &operator[](size_t i) { return mString.operator[](i); }
+
+  const char &operator[](size_t i) const { return mString.operator[](i); }
+
+  const char *c_str() const { return mString.c_str(); }
+
+  const char *constData() const { return mString.data(); }
+
+  const char *nullTerminated() const { return mString.c_str(); }
+
+  size_t size() const { return mString.size(); }
+
+  size_t length() const { return size(); }
+
+  void truncate(size_t len) {
+    if (mString.size() > len)
+      mString.resize(len);
+  }
+
+  void chop(size_t s) { mString.resize(size() - s); }
+
+  void resize(size_t len) { mString.resize(len); }
+
+  void reserve(size_t len) { mString.reserve(len); }
+
+  void prepend(const String &other) { mString.insert(0, other); }
+
+  void prepend(char ch) { mString.insert(0, &ch, 1); }
+
+  void insert(size_t pos, const String &text) {
+    mString.insert(pos, text.constData(), text.size());
+  }
+
+  void insert(size_t pos, const char *str, size_t len = npos) {
+    if (str) {
+      if (len == npos)
+        len = strlen(str);
+      mString.insert(pos, str, len);
+    }
+  }
+
+  void insert(size_t pos, char ch) { mString.insert(pos, &ch, 1); }
+
+  void append(char ch) { mString += ch; }
+
+  void append(const String &ba) { mString.append(ba); }
+
+  String compress() const;
+  String uncompress() const { return uncompress(constData(), size()); }
+  static String uncompress(const char *data, size_t size);
+
+  void append(const char *str, size_t len = npos) {
+    if (len == npos)
+      len = strlen(str);
+    if (len > 0)
+      mString.append(str, len);
+  }
+
+  size_t remove(const String &str, CaseSensitivity cs = CaseSensitive) {
+    size_t idx = 0;
+    size_t ret = 0;
+    while (true) {
+      idx = indexOf(str, idx, cs);
+      if (idx == npos)
+        break;
+      ++ret;
+      remove(idx, str.size());
+    }
+    return ret;
+  }
+  size_t remove(char ch, CaseSensitivity cs = CaseSensitive) {
+    size_t idx = 0;
+    size_t ret = 0;
+    while (true) {
+      idx = indexOf(ch, idx, cs);
+      if (idx == npos)
+        break;
+      ++ret;
+      remove(idx, 1);
+    }
+    return ret;
+  }
+
+  iterator erase(const_iterator begin, const_iterator end) {
+#ifdef HAVE_STRING_ITERATOR_ERASE
+    return mString.erase(begin, end);
+#else
+    if (begin >= end) {
+      return mString.end();
+    }
+
+    const size_t offset = begin - mString.begin();
+    mString.erase(offset, end - begin);
+    return mString.begin() + offset;
+#endif
+  }
+
+  String &erase(size_t index = 0, size_t count = npos) {
+    mString.erase(index, count);
+    return *this;
+  }
+
+  void remove(size_t idx, size_t count) { mString.erase(idx, count); }
+
+  String &operator+=(char ch) {
+    mString += ch;
+    return *this;
+  }
+
+  String &operator+=(const char *cstr) {
+    if (cstr)
+      mString += cstr;
+    return *this;
+  }
+
+  String &operator+=(const String &other) {
+    mString += other.mString;
+    return *this;
+  }
+
+  size_t compare(const String &other,
+                 CaseSensitivity cs = CaseSensitive) const {
+    if (cs == CaseSensitive)
+      return mString.compare(other.mString);
+    return strcasecmp(mString.c_str(), other.mString.c_str());
+  }
+
+  bool operator==(const String &other) const {
+    return mString == other.mString;
+  }
+
+  bool operator==(const char *other) const {
+    return other && !mString.compare(other);
+  }
+
+  bool operator!=(const String &other) const {
+    return mString != other.mString;
+  }
+
+  bool operator!=(const char *other) const {
+    return !other || mString.compare(other);
+  }
+
+  bool operator<(const String &other) const { return mString < other.mString; }
+
+  bool operator>(const String &other) const { return mString > other.mString; }
+
+  bool endsWith(char ch, CaseSensitivity c = CaseSensitive) const {
+    const size_t s = mString.size();
+    if (s) {
+      return (c == CaseInsensitive ? tolower(at(s - 1)) == tolower(ch)
+                                   : at(s - 1) == ch);
+    }
+    return false;
+  }
+
+  bool startsWith(char ch, CaseSensitivity c = CaseSensitive) const {
+    if (!isEmpty()) {
+      return (c == CaseInsensitive ? tolower(at(0)) == tolower(ch)
+                                   : at(0) == ch);
+    }
+    return false;
+  }
+
+  bool endsWith(const String &str, CaseSensitivity cs = CaseSensitive) const {
+    return endsWith(str.constData(), str.size(), cs);
+  }
+
+  bool endsWith(const char *str, size_t len = npos,
+                CaseSensitivity cs = CaseSensitive) const {
+    if (len == npos)
+      len = strlen(str);
+    const size_t s = mString.size();
+    if (s >= len) {
+      return (cs == CaseInsensitive
+                  ? !strncasecmp(str, constData() + s - len, len)
+                  : !strncmp(str, constData() + s - len, len));
+    }
+    return false;
+  }
+
+  bool startsWith(const String &str, CaseSensitivity cs = CaseSensitive) const 
{
+    return startsWith(str.constData(), str.size(), cs);
+  }
+
+  bool startsWith(const char *str, size_t len = npos,
+                  CaseSensitivity cs = CaseSensitive) const {
+    const size_t s = mString.size();
+    if (len == npos)
+      len = strlen(str);
+    if (s >= len) {
+      return (cs == CaseInsensitive ? !strncasecmp(str, constData(), len)
+                                    : !strncmp(str, constData(), len));
+    }
+    return false;
+  }
+
+  void replace(size_t idx, size_t len, const String &with) {
+    mString.replace(idx, len, with.mString);
+  }
+
+  size_t replace(const String &from, const String &to,
+                 CaseSensitivity cs = CaseSensitive) {
+    size_t idx = 0;
+    size_t ret = 0;
+    while (true) {
+      idx = indexOf(from, idx, cs);
+      if (idx == npos)
+        break;
+      ++ret;
+      replace(idx, from.size(), to);
+      idx += to.size();
+    }
+    return ret;
+  }
+
+  size_t replace(char from, char to, CaseSensitivity cs = CaseSensitive) {
+    size_t count = 0;
+    int i = static_cast<int>(size() - 1);
+    if (cs == CaseSensitive) {
+      while (i >= 0) {
+        char &ch = operator[](i);
+        if (ch == from) {
+          ch = to;
+          ++count;
+        }
+        --i;
+      }
+    } else {
+      from = tolower(from);
+      while (i >= 0) {
+        char &ch = operator[](i);
+        if (tolower(ch) == from) {
+          ch = to;
+          ++count;
+        }
+        --i;
+      }
+    }
+    return count;
+  }
+
+  String mid(size_t from, size_t l = npos) const {
+    if (l == npos)
+      l = size() - from;
+    if (from == 0 && l == size())
+      return *this;
+    return mString.substr(from, l);
+  }
+
+  String left(size_t l) const { return mString.substr(0, l); }
+
+  String right(size_t l) const { return mString.substr(size() - l, l); }
+
+  operator std::string() const { return mString; }
+
+  std::string &ref() { return mString; }
+
+  const std::string &ref() const { return mString; }
+
+  enum SplitFlag { NoSplitFlag = 0x0, SkipEmpty = 0x1, KeepSeparators = 0x2 };
+  List<String> split(char ch, unsigned int flags = NoSplitFlag) const {
+    List<String> ret;
+    if (!isEmpty()) {
+      size_t prev = 0;
+      const size_t add = flags & KeepSeparators ? 1 : 0;
+      while (1) {
+        const size_t next = indexOf(ch, prev);
+        if (next == npos)
+          break;
+        if (next > prev || !(flags & SkipEmpty))
+          ret.append(mid(prev, next - prev + add));
+        prev = next + 1;
+      }
+      if (prev < size() || !(flags & SkipEmpty))
+        ret.append(mid(prev));
+    }
+    return ret;
+  }
+
+  List<String> split(const String &str,
+                     unsigned int flags = NoSplitFlag) const {
+    List<String> ret;
+    size_t prev = 0;
+    while (1) {
+      const size_t next = indexOf(str, prev);
+      if (next == npos)
+        break;
+      if (next > prev || !(flags & SkipEmpty))
+        ret.append(mid(prev, next - prev));
+      prev = next + str.size();
+    }
+    if (prev < size() || !(flags & SkipEmpty))
+      ret.append(mid(prev));
+    return ret;
+  }
+
+  unsigned long long toULongLong(bool *ok = 0, size_t base = 10) const {
+    errno = 0;
+    char *end = 0;
+    const unsigned long long ret = ::strtoull(constData(), &end, base);
+    if (ok)
+      *ok = !errno && !*end;
+    return ret;
+  }
+  long long toLongLong(bool *ok = 0, size_t base = 10) const {
+    errno = 0;
+    char *end = 0;
+    const long long ret = ::strtoll(constData(), &end, base);
+    if (ok)
+      *ok = !errno && !*end;
+    return ret;
+  }
+  uint32_t toULong(bool *ok = 0, size_t base = 10) const {
+    errno = 0;
+    char *end = 0;
+    const uint32_t ret = ::strtoul(constData(), &end, base);
+    if (ok)
+      *ok = !errno && !*end;
+    return ret;
+  }
+  int32_t toLong(bool *ok = 0, size_t base = 10) const {
+    errno = 0;
+    char *end = 0;
+    const int32_t ret = ::strtol(constData(), &end, base);
+    if (ok)
+      *ok = !errno && !*end;
+    return ret;
+  }
+
+  enum TimeFormat { DateTime, Time, Date };
+
+  static String formatTime(time_t t, TimeFormat fmt = DateTime) {
+    const char *format = 0;
+    switch (fmt) {
+    case DateTime:
+      format = "%Y-%m-%d %H:%M:%S";
+      break;
+    case Date:
+      format = "%Y-%m-%d";
+      break;
+    case Time:
+      format = "%H:%M:%S";
+      break;
+    }
+
+    char buf[32];
+#ifdef _WIN32
+    tm *tm = localtime(&t);
+    const size_t w = strftime(buf, sizeof(buf), format, tm);
+#else
+    tm tm;
+    localtime_r(&t, &tm);
+    const size_t w = strftime(buf, sizeof(buf), format, &tm);
+#endif
+    return String(buf, w);
+  }
+
+  String toHex() const { return toHex(*this); }
+  static String toHex(const String &hex) {
+    return toHex(hex.constData(), hex.size());
+  }
+  static String toHex(const void *data, size_t len);
+
+  static String number(char num, size_t base = 10) {
+    return String::number(static_cast<long long>(num), base);
+  }
+  static String number(unsigned char num, size_t base = 10) {
+    return String::number(static_cast<long long>(num), base);
+  }
+  static String number(short num, size_t base = 10) {
+    return String::number(static_cast<long long>(num), base);
+  }
+  static String number(unsigned short num, size_t base = 10) {
+    return String::number(static_cast<long long>(num), base);
+  }
+  static String number(int num, size_t base = 10) {
+    return String::number(static_cast<long long>(num), base);
+  }
+  static String number(unsigned int num, size_t base = 10) {
+    return String::number(static_cast<long long>(num), base);
+  }
+  static String number(long num, size_t base = 10) {
+    return String::number(static_cast<long long>(num), base);
+  }
+  static String number(unsigned long num, size_t base = 10) {
+    return String::number(static_cast<unsigned long long>(num), base);
+  }
+  static String number(long long num, size_t base = 10) {
+    const char *format = 0;
+    switch (base) {
+    case 10:
+      format = "%lld";
+      break;
+    case 16:
+      format = "0x%llx";
+      break;
+    case 8:
+      format = "%llo";
+      break;
+    case 1: {
+      String ret;
+      while (num) {
+        ret.append(num & 1 ? '1' : '0');
+        num >>= 1;
+      }
+      return ret;
+    }
+    default:
+      assert(0);
+      return String();
+    }
+    char buf[32];
+    const size_t w = ::snprintf(buf, sizeof(buf), format, num);
+    return String(buf, w);
+  }
+
+  static String number(unsigned long long num, size_t base = 10) {
+    const char *format = 0;
+    switch (base) {
+    case 10:
+      format = "%llu";
+      break;
+    case 16:
+      format = "0x%llx";
+      break;
+    case 8:
+      format = "%llo";
+      break;
+    case 1: {
+      String ret;
+      while (num) {
+        ret.append(num & 1 ? '1' : '0');
+        num >>= 1;
+      }
+      return ret;
+    }
+    default:
+      assert(0);
+      return String();
+    }
+    char buf[32];
+    const size_t w = ::snprintf(buf, sizeof(buf), format, num);
+    return String(buf, w);
+  }
+
+  static String number(double num, size_t prec = 2) {
+    char format[32];
+
+    // cast to unsigned because windows doesn't have %z format specifier
+    snprintf(format, sizeof(format), "%%.%uf", (unsigned)prec);
+    char buf[32];
+    const size_t w = ::snprintf(buf, sizeof(buf), format, num);
+    return String(buf, w);
+  }
+
+  static String join(const List<String> &list, char ch) {
+    return String::join(list, String(&ch, 1));
+  }
+
+  static String join(const List<String> &list, const String &sep) {
+    String ret;
+    const size_t sepSize = sep.size();
+    size_t size = std::max<size_t>(0, list.size() - 1) * sepSize;
+    const size_t count = list.size();
+    for (size_t i = 0; i < count; ++i)
+      size += list.at(i).size();
+    ret.reserve(size);
+    for (size_t i = 0; i < count; ++i) {
+      const String &b = list.at(i);
+      ret.append(b);
+      if (sepSize && i + 1 < list.size())
+        ret.append(sep);
+    }
+    return ret;
+  }
+  template <size_t StaticBufSize = 4096>
+  static String format(const char *format, ...) RCT_PRINTF_WARNING(1, 2);
+
+  template <size_t StaticBufSize = 4096>
+  static String format(const char *format, va_list args) {
+    va_list copy;
+    va_copy(copy, args);
+
+    char buffer[StaticBufSize];
+    const size_t size = ::vsnprintf(buffer, StaticBufSize, format, args);
+    assert(size >= 0);
+    String ret;
+    if (size < StaticBufSize) {
+      ret.assign(buffer, size);
+    } else {
+      ret.resize(size);
+      ::vsnprintf(&ret[0], size + 1, format, copy);
+    }
+    va_end(copy);
+    return ret;
+  }
+
+private:
+  std::string mString;
+};
+
+template class String<int>;
+
+// Local Variables:
+// beardbolt-command: "g++ -std=c++17 -O3"
+// rmsbolt-command: "g++ -std=c++17 -O3"
+// beardbolt-disassemble: nil
+// beardbolt-preserve-library-functions: t
+// rmsbolt-filter-directives: t
+// beardbolt-asm-format: att
+// rmsbolt-asm-format: "att"
+// beardbolt-preserve-directives: nil
+// End:



reply via email to

[Prev in Thread] Current Thread [Next in Thread]