diff --git a/py/builtin.c b/py/builtin.c
index f102aa5885adf3edbba022cb873c600f062c6a25..8340ad3045881e19fdb8eb41452a8434d9dcc0e8 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -8,7 +8,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "runtime.h"
@@ -139,8 +139,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_callable_obj, mp_builtin_callable);
 static mp_obj_t mp_builtin_chr(mp_obj_t o_in) {
     int ord = mp_obj_get_int(o_in);
     if (0 <= ord && ord <= 0x10ffff) {
-        char str[2] = {ord, '\0'};
-        return mp_obj_new_str(qstr_from_strn_copy(str, 1));
+        char str[1] = {ord};
+        return mp_obj_new_str(qstr_from_strn(str, 1));
     } else {
         nlr_jump(mp_obj_new_exception_msg(MP_QSTR_ValueError, "chr() arg not in range(0x110000)"));
     }
@@ -257,11 +257,12 @@ static mp_obj_t mp_builtin_next(mp_obj_t o) {
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_next_obj, mp_builtin_next);
 
 static mp_obj_t mp_builtin_ord(mp_obj_t o_in) {
-    const char *str = qstr_str(mp_obj_get_qstr(o_in));
-    if (strlen(str) == 1) {
+    uint len;
+    const byte *str = qstr_data(mp_obj_get_qstr(o_in), &len);
+    if (len == 1) {
         return mp_obj_new_int(str[0]);
     } else {
-        nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "ord() expected a character, but string of length %d found", strlen(str)));
+        nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "ord() expected a character, but string of length %d found", len));
     }
 }
 
@@ -304,7 +305,8 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_range_obj, 1, 3, mp_builtin_range
 static mp_obj_t mp_builtin_repr(mp_obj_t o_in) {
     vstr_t *vstr = vstr_new();
     mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))vstr_printf, vstr, o_in, PRINT_REPR);
-    return mp_obj_new_str(qstr_from_str_take(vstr->buf, vstr->alloc));
+    // TODO don't intern this string
+    return mp_obj_new_str(qstr_from_strn_take(vstr->buf, vstr->alloc, vstr->len));
 }
 
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_repr_obj, mp_builtin_repr);
@@ -343,7 +345,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_sorted_obj, 1, mp_builtin_sorted);
 static mp_obj_t mp_builtin_str(mp_obj_t o_in) {
     vstr_t *vstr = vstr_new();
     mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf, vstr, o_in, PRINT_STR);
-    return mp_obj_new_str(qstr_from_str_take(vstr->buf, vstr->alloc));
+    // TODO don't intern this string
+    return mp_obj_new_str(qstr_from_strn_take(vstr->buf, vstr->alloc, vstr->len));
 }
 
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_str_obj, mp_builtin_str);
diff --git a/py/builtineval.c b/py/builtineval.c
index c7bd6b6298e3e4ea6ba09beab817907b52bae875..67072a0fa7168510bbd653bd4523d8e12bb6281b 100644
--- a/py/builtineval.c
+++ b/py/builtineval.c
@@ -8,6 +8,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "lexerunix.h"
 #include "parse.h"
@@ -19,10 +20,11 @@
 #include "builtin.h"
 
 static mp_obj_t mp_builtin_eval(mp_obj_t o_in) {
-    const char *str = qstr_str(mp_obj_get_qstr(o_in));
+    uint str_len;
+    const byte *str = qstr_data(mp_obj_get_qstr(o_in), &str_len);
 
     // create the lexer
-    mp_lexer_t *lex = mp_lexer_new_from_str_len("<string>", str, strlen(str), 0);
+    mp_lexer_t *lex = mp_lexer_new_from_str_len("<string>", (const char*)str, str_len, 0);
 
     // parse the string
     qstr parse_exc_id;
diff --git a/py/builtinimport.c b/py/builtinimport.c
index 92d5d5ac9f81bd8e2504c47ed77438c597567f6c..4cdad4e249ecb160034d46b2c523f943e2750944 100644
--- a/py/builtinimport.c
+++ b/py/builtinimport.c
@@ -8,6 +8,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "lexerunix.h"
 #include "parse.h"
diff --git a/py/builtinmp.c b/py/builtinmp.c
index f72a80f1a84e944fd321b3327e240fa4e42199a4..dfbea0906e1955f9570ddb2e4de57cefd7ab8d3b 100644
--- a/py/builtinmp.c
+++ b/py/builtinmp.c
@@ -7,7 +7,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 #include "builtin.h"
@@ -38,8 +38,8 @@ void mp_module_micropython_init(void) {
     rt_store_name(MP_QSTR_micropython, m_mp);
 
 #if MICROPY_MEM_STATS
-    rt_store_attr(m_mp, qstr_from_str_static("mem_total"), (mp_obj_t)&mp_builtin_mem_total_obj);
-    rt_store_attr(m_mp, qstr_from_str_static("mem_current"), (mp_obj_t)&mp_builtin_mem_current_obj);
-    rt_store_attr(m_mp, qstr_from_str_static("mem_peak"), (mp_obj_t)&mp_builtin_mem_peak_obj);
+    rt_store_attr(m_mp, QSTR_FROM_STR_STATIC("mem_total"), (mp_obj_t)&mp_builtin_mem_total_obj);
+    rt_store_attr(m_mp, QSTR_FROM_STR_STATIC("mem_current"), (mp_obj_t)&mp_builtin_mem_current_obj);
+    rt_store_attr(m_mp, QSTR_FROM_STR_STATIC("mem_peak"), (mp_obj_t)&mp_builtin_mem_peak_obj);
 #endif
 }
diff --git a/py/compile.c b/py/compile.c
index 27b2439639e8634630c9c9777d64faa5ef704344..f61c4580c7847ad1819c9f869788210e41053166 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -7,7 +7,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
@@ -273,8 +273,8 @@ static bool cpython_c_tuple_is_const(mp_parse_node_t pn) {
 }
 
 static void cpython_c_print_quoted_str(vstr_t *vstr, qstr qstr, bool bytes) {
-    const char *str = qstr_str(qstr);
-    int len = strlen(str);
+    uint len;
+    const byte *str = qstr_data(qstr, &len);
     bool has_single_quote = false;
     bool has_double_quote = false;
     for (int i = 0; i < len; i++) {
@@ -1169,22 +1169,20 @@ void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q1, qstr *q2) {
             int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
             int len = n - 1;
             for (int i = 0; i < n; i++) {
-                len += strlen(qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])));
+                len += qstr_len(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
             }
-            char *str = m_new(char, len + 1);
-            char *str_dest = str;
-            str[0] = 0;
+            byte *q_ptr;
+            byte *str_dest = qstr_build_start(len, &q_ptr);
             for (int i = 0; i < n; i++) {
                 if (i > 0) {
                     *str_dest++ = '.';
                 }
-                const char *str_src = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
-                size_t str_src_len = strlen(str_src);
+                uint str_src_len;
+                const byte *str_src = qstr_data(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]), &str_src_len);
                 memcpy(str_dest, str_src, str_src_len);
                 str_dest += str_src_len;
             }
-            *str_dest = '\0';
-            *q2 = qstr_from_str_take(str, len + 1);
+            *q2 = qstr_build_end(q_ptr);
             EMIT(import_name, *q2);
             if (is_as) {
                 for (int i = 1; i < n; i++) {
@@ -1221,7 +1219,7 @@ void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) {
 #if MICROPY_EMIT_CPYTHON
         EMIT(load_const_verbatim_str, "('*',)");
 #else
-        EMIT(load_const_str, qstr_from_str_static("*"), false);
+        EMIT(load_const_str, QSTR_FROM_STR_STATIC("*"), false);
         EMIT(build_tuple, 1);
 #endif
 
@@ -1248,7 +1246,9 @@ void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) {
                     vstr_printf(vstr, ", ");
                 }
                 vstr_printf(vstr, "'");
-                vstr_printf(vstr, qstr_str(id2));
+                uint len;
+                const byte *str = qstr_data(id2, &len);
+                vstr_add_strn(vstr, (const char*)str, len);
                 vstr_printf(vstr, "'");
             }
             if (n == 1) {
@@ -2128,24 +2128,21 @@ void compile_atom_string(compiler_t *comp, mp_parse_node_struct_t *pns) {
             printf("SyntaxError: cannot mix bytes and nonbytes literals\n");
             return;
         }
-        const char *str = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
-        n_bytes += strlen(str);
+        n_bytes += qstr_len(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
     }
 
-    // allocate memory for concatenated string/bytes
-    char *cat_str = m_new(char, n_bytes + 1);
-
     // concatenate string/bytes
-    char *s_dest = cat_str;
+    byte *q_ptr;
+    byte *s_dest = qstr_build_start(n_bytes, &q_ptr);
     for (int i = 0; i < n; i++) {
-        const char *s = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
-        size_t s_len = strlen(s);
+        uint s_len;
+        const byte *s = qstr_data(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]), &s_len);
         memcpy(s_dest, s, s_len);
         s_dest += s_len;
     }
-    *s_dest = '\0';
+    qstr q = qstr_build_end(q_ptr);
 
-    EMIT(load_const_str, qstr_from_str_take(cat_str, n_bytes + 1), string_kind == MP_PARSE_NODE_BYTES);
+    EMIT(load_const_str, q, string_kind == MP_PARSE_NODE_BYTES);
 }
 
 // pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node
@@ -2767,7 +2764,7 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
         assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
         mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t*)pns->nodes[1];
 
-        qstr qstr_arg = qstr_from_str_static(".0");
+        qstr qstr_arg = QSTR_FROM_STR_STATIC(".0");
         if (comp->pass == PASS_1) {
             bool added;
             id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qstr_arg, &added);
diff --git a/py/emitbc.c b/py/emitbc.c
index 1f034e9df1091dfb1ba8e4bf3be26ddf63514a75..c3385e0b6665fb37f4c172afc954be6c7528783a 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -7,6 +7,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
diff --git a/py/emitcommon.c b/py/emitcommon.c
index e30cad74963843a0d48cbc6df5dfc3868dc10a2d..bfcdad79735b5d6e5224eefafc0ef8005e3ae2b6 100644
--- a/py/emitcommon.c
+++ b/py/emitcommon.c
@@ -6,6 +6,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
diff --git a/py/emitcpy.c b/py/emitcpy.c
index 42eef91d11d661180f1d5be7eaab5044c4cf4922..de2a5784dbb2d139b0d996c1c532e90ad8daac9f 100644
--- a/py/emitcpy.c
+++ b/py/emitcpy.c
@@ -7,6 +7,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c
index 073dfa06040c2a117e18a72384cf3043eca26b76..9d5a4206a0ef2462fbd37da1969141a9854a7dff 100644
--- a/py/emitinlinethumb.c
+++ b/py/emitinlinethumb.c
@@ -7,6 +7,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
diff --git a/py/emitnative.c b/py/emitnative.c
index a80cd2cf10f8cc04434b384f2e13be57df258013..6fc17424899af16744639832e30321b87f66c9f6 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -25,6 +25,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
diff --git a/py/emitpass1.c b/py/emitpass1.c
index c73522e47480900f1db5196fa71f903feb282104..38115a51c1358b7a7e3ab4cfbafc61389c3d0e1f 100644
--- a/py/emitpass1.c
+++ b/py/emitpass1.c
@@ -7,7 +7,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
diff --git a/py/lexer.c b/py/lexer.c
index e8c6bc3082e3695560689e2aa87ca2c90be34e8b..daaeebf5118ec11499c606e4759465611992b836 100644
--- a/py/lexer.c
+++ b/py/lexer.c
@@ -7,6 +7,8 @@
 #include <assert.h>
 
 #include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 
 #define TAB_SIZE (8)
@@ -593,7 +595,7 @@ static void mp_lexer_next_token_into(mp_lexer_t *lex, mp_token_t *tok, bool firs
 mp_lexer_t *mp_lexer_new(const char *src_name, void *stream_data, mp_lexer_stream_next_char_t stream_next_char, mp_lexer_stream_close_t stream_close) {
     mp_lexer_t *lex = m_new(mp_lexer_t, 1);
 
-    lex->source_name = qstr_from_strn_copy(src_name, strlen(src_name));
+    lex->source_name = qstr_from_str(src_name);
     lex->stream_data = stream_data;
     lex->stream_next_char = stream_next_char;
     lex->stream_close = stream_close;
diff --git a/py/lexerstr.c b/py/lexerstr.c
index b8594f4205ca34ecaeeb193196c1912b8c4eec30..8cbf36d8314d987ca0b3522bef393499f5f86058 100644
--- a/py/lexerstr.c
+++ b/py/lexerstr.c
@@ -2,6 +2,8 @@
 #include <stdio.h>
 
 #include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 
 typedef struct _mp_lexer_str_buf_t {
diff --git a/py/lexerunix.c b/py/lexerunix.c
index 225ed20a7f2ccd4afceb80722fa038d4fe4a0558..7846120a4a36d7db0295f9d22507e22cf4fe61ab 100644
--- a/py/lexerunix.c
+++ b/py/lexerunix.c
@@ -5,6 +5,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "lexerunix.h"
 
diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e74ea84191942cd153ff8c4984657098cb6713b
--- /dev/null
+++ b/py/makeqstrdata.py
@@ -0,0 +1,62 @@
+import argparse
+import re
+
+# this must match the equivalent function in qstr.c
+def compute_hash(qstr):
+    hash = 0
+    for char in qstr:
+        hash += ord(char)
+    return hash & 0xffff
+
+def do_work(infiles):
+    # read the qstrs in from the input files
+    qstrs = []
+    for infile in infiles:
+        with open(infile, 'rt') as f:
+            line_number = 0
+            for line in f:
+                line_number += 1
+                line = line.strip()
+
+                # ignore blank lines and comments
+                if len(line) == 0 or line.startswith('//'):
+                    continue
+
+                # verify line is of the correct form
+                match = re.match(r'Q\(([0-9A-Za-z_]+)\)$', line)
+                if not match:
+                    print('({}:{}) bad qstr format, got {}'.format(infile, line_number, line))
+                    return False
+
+                # get the qstr value
+                qstr = match.group(1)
+
+                # don't add duplicates
+                if qstr in qstrs:
+                    continue
+
+                # add the qstr to the list
+                qstrs.append(qstr)
+
+    # process the qstrs, printing out the generated C header file
+    print('// This file was automatically generated by makeqstrdata.py')
+    print()
+    for qstr in qstrs:
+        qhash = compute_hash(qstr)
+        qlen = len(qstr)
+        print('Q({}, (const byte*)"\\x{:02x}\\x{:02x}\\x{:02x}\\x{:02x}" "{}")'.format(qstr, qhash & 0xff, (qhash >> 8) & 0xff, qlen & 0xff, (qlen >> 8) & 0xff, qstr))
+
+    return True
+
+def main():
+    arg_parser = argparse.ArgumentParser(description='Process raw qstr file and output qstr data with length, hash and data bytes')
+    arg_parser.add_argument('files', nargs='+', help='input file(s)')
+    args = arg_parser.parse_args()
+
+    result = do_work(args.files)
+    if not result:
+        print('exiting with error code')
+        exit(1)
+
+if __name__ == "__main__":
+    main()
diff --git a/py/map.c b/py/map.c
index 1ce763ab0e251f0cf02197bcd0df0e076fbf99a2..9f919e06ae32a40eac123b7f6234087772e1d904 100644
--- a/py/map.c
+++ b/py/map.c
@@ -4,6 +4,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "map.h"
diff --git a/py/misc.h b/py/misc.h
index 5b012d03dc2fdedb8e07b754e895842add451538..8756c25a0792ece8597e7006b13ee8217c72b603 100644
--- a/py/misc.h
+++ b/py/misc.h
@@ -88,14 +88,4 @@ void vstr_printf(vstr_t *vstr, const char *fmt, ...);
 void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap);
 #endif
 
-/** unique string ***********************************************/
-
-typedef unsigned int qstr;
-
-void qstr_init(void);
-qstr qstr_from_str_static(const char *str);
-qstr qstr_from_str_take(char *str, int alloc_len);
-qstr qstr_from_strn_copy(const char *str, int len);
-const char* qstr_str(qstr qstr);
-
 #endif // _INCLUDED_MINILIB_H
diff --git a/py/mpqstr.h b/py/mpqstr.h
deleted file mode 100644
index 1440fb3b80fa35cf6e1ce417fd81760d00b88d09..0000000000000000000000000000000000000000
--- a/py/mpqstr.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// See mpqstrraw.h for a list of qstr's that are available as constants.
-// Reference them as MP_QSTR_xxxx.
-//
-// Note: it would be possible to define MP_QSTR_xxx as qstr_from_str_static("xxx")
-// for qstrs that are referenced this way, but you don't want to have them in ROM.
-
-enum {
-    MP_QSTR_nil = 0,
-#define Q(id) MP_QSTR_##id,
-#include "mpqstrraw.h"
-#undef Q
-    MP_QSTR_number_of,
-} category_t;
diff --git a/py/obj.c b/py/obj.c
index 42f86cf175545f28c975011093b0d95c9d8b3648..5a6c08332a417371d80049c029cc54f8be933eb0 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -8,7 +8,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "runtime.h"
@@ -268,7 +268,7 @@ uint mp_get_index(const mp_obj_type_t *type, machine_uint_t len, mp_obj_t index)
 mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) {
     mp_small_int_t len = 0;
     if (MP_OBJ_IS_TYPE(o_in, &str_type)) {
-        len = strlen(qstr_str(mp_obj_str_get(o_in)));
+        len = qstr_len(mp_obj_str_get(o_in));
     } else if (MP_OBJ_IS_TYPE(o_in, &tuple_type)) {
         uint seq_len;
         mp_obj_t *seq_items;
diff --git a/py/objarray.c b/py/objarray.c
index a321e2a8e5e3fede9a683366c84832577cd3a3ac..a054c8f9806ec710a0be38dba2a05798bcbfeb3e 100644
--- a/py/objarray.c
+++ b/py/objarray.c
@@ -7,7 +7,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "map.h"
 #include "runtime0.h"
diff --git a/py/objbool.c b/py/objbool.c
index fac26f31e75a2f58d949d835f5b13cf07e015652..729ffb4e6d487fe7b1425eabd87025e69ac3521d 100644
--- a/py/objbool.c
+++ b/py/objbool.c
@@ -4,7 +4,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/objboundmeth.c b/py/objboundmeth.c
index 2b132004e21a50d90b32a9c80011da7d9bc1d6ad..500e61bd53bee9491c46b35c42f7c63fb7cc4973 100644
--- a/py/objboundmeth.c
+++ b/py/objboundmeth.c
@@ -6,6 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/objcell.c b/py/objcell.c
index 264125bf3a0faae5397732fa767233e3d6101523..04c7f368511fad5b0cc359abc6b951ad14dc5c42 100644
--- a/py/objcell.c
+++ b/py/objcell.c
@@ -5,6 +5,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/objclosure.c b/py/objclosure.c
index 7f6bcf4accf834741912215007adb3e251721584..0f4816e5fd61238e6ece32b6bc30da0f18adc401 100644
--- a/py/objclosure.c
+++ b/py/objclosure.c
@@ -6,6 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/objcomplex.c b/py/objcomplex.c
index b56f75c4cd5bd2687117dc626e69c94fd7b6a12b..af148a278632e3ff97b4304d3ac5c9701d3a306e 100644
--- a/py/objcomplex.c
+++ b/py/objcomplex.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "map.h"
diff --git a/py/objdict.c b/py/objdict.c
index 9493bc89b155a97269a2462131d6a75149f2e58c..55a612913d0149cd37e315f779e2f1e14bb3df8f 100644
--- a/py/objdict.c
+++ b/py/objdict.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "runtime.h"
diff --git a/py/objenumerate.c b/py/objenumerate.c
index 3e25124c49d96747a38f4644380ed4e9e08878bc..564cb1c47420423e8ef594c6c174fa1a07bc8cd6 100644
--- a/py/objenumerate.c
+++ b/py/objenumerate.c
@@ -3,6 +3,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/objexcept.c b/py/objexcept.c
index 2e145ee358e1294e954813483574eff434b7df45..c91b71dd9eb6baa3d758a2cb2f9a67f9d2285530 100644
--- a/py/objexcept.c
+++ b/py/objexcept.c
@@ -7,7 +7,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "objtuple.h"
 
@@ -100,7 +100,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(qstr id, const char *fmt, ...) {
         va_start(ap, fmt);
         vstr_vprintf(vstr, fmt, ap);
         va_end(ap);
-        o->msg = qstr_from_str_take(vstr->buf, vstr->alloc);
+        o->msg = qstr_from_strn_take(vstr->buf, vstr->alloc, vstr->len);
     }
 
     return o;
diff --git a/py/objfilter.c b/py/objfilter.c
index 6ef3ef62d472bdd54c7c1ad26ab21ada0ad2c6ac..bfed2420f076f71e40d597fd692de5d66731d7a4 100644
--- a/py/objfilter.c
+++ b/py/objfilter.c
@@ -4,7 +4,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/objfloat.c b/py/objfloat.c
index d397daab279b623b9c5f8c3510fd993be371ee86..9f1f478cabda6caf6b5692ea68a4a9ffa59ace4c 100644
--- a/py/objfloat.c
+++ b/py/objfloat.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 
diff --git a/py/objfun.c b/py/objfun.c
index 0bac142dab828d76943d49329631efec7ceccf3c..b749860c25d7b6395530fc4f7fe2474678c60c18 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "map.h"
 #include "runtime.h"
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 2e8bd3d32834e08d68d897d43cfa21c5a700aaea..192e5c63285d2d94d39d55c9f03a7be23a7e1ef8 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 #include "bc.h"
diff --git a/py/objint.c b/py/objint.c
index 49341d38a2e1a1b402f1ebf4e7c460d6dcd189cf..02628b7ef9dad8bf6da1cc66505535910053d7c1 100644
--- a/py/objint.c
+++ b/py/objint.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "objint.h"
 
diff --git a/py/objint_longlong.c b/py/objint_longlong.c
index 24d693761e2acf910c19c0adcaa4ecc4b7bf109c..fd13a038b6f0249b1c9c57fde8bfbe45f2783860 100644
--- a/py/objint_longlong.c
+++ b/py/objint_longlong.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "objint.h"
 #include "runtime0.h"
diff --git a/py/objlist.c b/py/objlist.c
index 0ad7b687992e4416a625bf33f6701ee2849fc09b..bc363d38fdb5cc21597ed031d1f90d47c44993d5 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "map.h"
 #include "runtime0.h"
@@ -259,8 +259,8 @@ mp_obj_t mp_obj_list_sort(uint n_args, const mp_obj_t *args, mp_map_t *kwargs) {
     }
     mp_obj_list_t *self = args[0];
     if (self->len > 1) {
-        mp_map_elem_t *keyfun = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(qstr_from_str_static("key")), MP_MAP_LOOKUP);
-        mp_map_elem_t *reverse = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(qstr_from_str_static("reverse")), MP_MAP_LOOKUP);
+        mp_map_elem_t *keyfun = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(QSTR_FROM_STR_STATIC("key")), MP_MAP_LOOKUP);
+        mp_map_elem_t *reverse = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(QSTR_FROM_STR_STATIC("reverse")), MP_MAP_LOOKUP);
         mp_quicksort(self->items, self->items + self->len - 1,
                      keyfun ? keyfun->value : NULL,
                      reverse && reverse->value ? rt_is_true(reverse->value) : false);
diff --git a/py/objmap.c b/py/objmap.c
index 0c25cfdca3f1f70ea46506ac30e34f07194facf6..39778e11eede4604421e17719bb4da7b2fb0f113 100644
--- a/py/objmap.c
+++ b/py/objmap.c
@@ -4,7 +4,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/objmodule.c b/py/objmodule.c
index fb7842e5af698f2ecb1cf3c64a650336f373fede..73f146131e4b3fbcf3d02d439f783c35580b7251 100644
--- a/py/objmodule.c
+++ b/py/objmodule.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 #include "map.h"
diff --git a/py/objnone.c b/py/objnone.c
index 84d0ba164c27ede507d9b2bfcbf293f4f7e2b93f..ecc7c4b4e7ccd0f445a069a94129999703aad980 100644
--- a/py/objnone.c
+++ b/py/objnone.c
@@ -4,6 +4,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 
 typedef struct _mp_obj_none_t {
diff --git a/py/objrange.c b/py/objrange.c
index a2a0e67b00bc31a384b8580ff89658a0fc547106..1fff327ab70398fc80e9093048c68daf3d43e766 100644
--- a/py/objrange.c
+++ b/py/objrange.c
@@ -4,6 +4,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 
 /******************************************************************************/
diff --git a/py/objset.c b/py/objset.c
index a9ba2ad885678377b6d459334e536d56617be280..cf4545c2571112ae4dc0f5af700a37c386dc52de 100644
--- a/py/objset.c
+++ b/py/objset.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 #include "runtime0.h"
diff --git a/py/objslice.c b/py/objslice.c
index 8abcea08d065e78e46e1410ab7dc8ee2b0713e4a..d5c31f4461b0ca1d02ad9943a3d59c427378a08a 100644
--- a/py/objslice.c
+++ b/py/objslice.c
@@ -6,6 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 
diff --git a/py/objstr.c b/py/objstr.c
index 5e87097a82d905e8342f85c7e0ca0881b10e3b7b..3552058430533fd8bc2812a86ac88b0bf79ea040 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -7,7 +7,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "runtime.h"
@@ -36,9 +36,30 @@ void str_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj
     mp_obj_str_print_qstr(print, env, self->qstr, kind);
 }
 
+// like strstr but with specified length and allows \0 bytes
+// TODO replace with something more efficient/standard
+static const byte *find_subbytes(const byte *haystack, uint hlen, const byte *needle, uint nlen) {
+    if (hlen >= nlen) {
+        for (uint i = 0; i <= hlen - nlen; i++) {
+            bool found = true;
+            for (uint j = 0; j < nlen; j++) {
+                if (haystack[i + j] != needle[j]) {
+                    found = false;
+                    break;
+                }
+            }
+            if (found) {
+                return haystack + i;
+            }
+        }
+    }
+    return NULL;
+}
+
 mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     mp_obj_str_t *lhs = lhs_in;
-    const char *lhs_str = qstr_str(lhs->qstr);
+    uint lhs_len;
+    const byte *lhs_data = qstr_data(lhs->qstr, &lhs_len);
     switch (op) {
         case RT_BINARY_OP_SUBSCR:
             // TODO: need predicate to check for int-like type (bools are such for example)
@@ -46,31 +67,30 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
             if (MP_OBJ_IS_SMALL_INT(rhs_in)) {
                 // TODO: This implements byte string access for single index so far
                 // TODO: Handle negative indexes.
-                return mp_obj_new_int(lhs_str[mp_obj_get_int(rhs_in)]);
+                return mp_obj_new_int(lhs_data[mp_obj_get_int(rhs_in)]);
 #if MICROPY_ENABLE_SLICE
             } else if (MP_OBJ_IS_TYPE(rhs_in, &slice_type)) {
                 machine_int_t start, stop, step;
                 mp_obj_slice_get(rhs_in, &start, &stop, &step);
                 assert(step == 1);
-                int len = strlen(lhs_str);
                 if (start < 0) {
-                    start = len + start;
+                    start = lhs_len + start;
                     if (start < 0) {
                         start = 0;
                     }
-                } else if (start > len) {
-                    start = len;
+                } else if (start > lhs_len) {
+                    start = lhs_len;
                 }
                 if (stop <= 0) {
-                    stop = len + stop;
+                    stop = lhs_len + stop;
                     // CPython returns empty string in such case
                     if (stop < 0) {
                         stop = start;
                     }
-                } else if (stop > len) {
-                    stop = len;
+                } else if (stop > lhs_len) {
+                    stop = lhs_len;
                 }
-                return mp_obj_new_str(qstr_from_strn_copy(lhs_str + start, stop - start));
+                return mp_obj_new_str(qstr_from_strn((const char*)lhs_data + start, stop - start));
 #endif
             } else {
                 // Message doesn't match CPython, but we don't have so much bytes as they
@@ -82,24 +102,24 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
         case RT_BINARY_OP_INPLACE_ADD:
             if (MP_OBJ_IS_TYPE(rhs_in, &str_type)) {
                 // add 2 strings
-                const char *rhs_str = qstr_str(((mp_obj_str_t*)rhs_in)->qstr);
-                size_t lhs_len = strlen(lhs_str);
-                size_t rhs_len = strlen(rhs_str);
-                int alloc_len = lhs_len + rhs_len + 1;
-                char *val = m_new(char, alloc_len);
-                memcpy(val, lhs_str, lhs_len);
-                memcpy(val + lhs_len, rhs_str, rhs_len);
-                val[lhs_len + rhs_len] = '\0';
-                return mp_obj_new_str(qstr_from_str_take(val, alloc_len));
+                uint rhs_len;
+                const byte *rhs_data = qstr_data(((mp_obj_str_t*)rhs_in)->qstr, &rhs_len);
+                int alloc_len = lhs_len + rhs_len;
+                byte *q_ptr;
+                byte *val = qstr_build_start(alloc_len, &q_ptr);
+                memcpy(val, lhs_data, lhs_len);
+                memcpy(val + lhs_len, rhs_data, rhs_len);
+                return mp_obj_new_str(qstr_build_end(q_ptr));
             }
             break;
         case RT_COMPARE_OP_IN:
         case RT_COMPARE_OP_NOT_IN:
             /* NOTE `a in b` is `b.__contains__(a)` */
             if (MP_OBJ_IS_TYPE(rhs_in, &str_type)) {
-                const char *rhs_str = qstr_str(((mp_obj_str_t*)rhs_in)->qstr);
-                /* FIXME \0 in strs */
-                return MP_BOOL((op == RT_COMPARE_OP_IN) ^ (strstr(lhs_str, rhs_str) == NULL));
+                uint rhs_len;
+                const byte *rhs_data = qstr_data(((mp_obj_str_t*)rhs_in)->qstr, &rhs_len);
+                return MP_BOOL((op == RT_COMPARE_OP_IN) ^ (find_subbytes(lhs_data, lhs_len, rhs_data, rhs_len) == NULL));
+                return mp_const_false;
             }
             break;
     }
@@ -143,22 +163,22 @@ mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) {
     }
 
     // make joined string
-    char *joined_str = m_new(char, required_len + 1);
-    char *s_dest = joined_str;
+    byte *q_ptr;
+    byte *s_dest = qstr_build_start(required_len, &q_ptr);
     for (int i = 0; i < seq_len; i++) {
         if (i > 0) {
             memcpy(s_dest, sep_str, sep_len);
             s_dest += sep_len;
         }
-        const char *s2 = qstr_str(mp_obj_str_get(seq_items[i]));
-        size_t s2_len = strlen(s2);
+        uint s2_len;
+        const byte *s2 = qstr_data(mp_obj_str_get(seq_items[i]), &s2_len);
         memcpy(s_dest, s2, s2_len);
         s_dest += s2_len;
     }
-    *s_dest = '\0';
+    qstr q = qstr_build_end(q_ptr);
 
     // return joined string
-    return mp_obj_new_str(qstr_from_str_take(joined_str, required_len + 1));
+    return mp_obj_new_str(q);
 
 bad_arg:
     nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "?str.join expecting a list of str's"));
@@ -246,20 +266,14 @@ mp_obj_t str_strip(uint n_args, const mp_obj_t *args) {
     }
 
     if (first_good_char_pos == 0 && last_good_char_pos == 0) {
-        //string is all whitespace, return '\0'
-        char *empty = m_new(char, 1);
-        empty[0] = '\0';
-        return mp_obj_new_str(qstr_from_str_take(empty, 1));
+        //string is all whitespace, return ''
+        return mp_obj_new_str(MP_QSTR_);
     }
 
     assert(last_good_char_pos >= first_good_char_pos);
     //+1 to accomodate the last character
     size_t stripped_len = last_good_char_pos - first_good_char_pos + 1;
-    //+1 to accomodate '\0'
-    char *stripped_str = m_new(char, stripped_len + 1);
-    memcpy(stripped_str, orig_str + first_good_char_pos, stripped_len);
-    stripped_str[stripped_len] = '\0';
-    return mp_obj_new_str(qstr_from_str_take(stripped_str, stripped_len + 1));
+    return mp_obj_new_str(qstr_from_strn(orig_str + first_good_char_pos, stripped_len));
 }
 
 mp_obj_t str_format(uint n_args, const mp_obj_t *args) {
@@ -288,7 +302,7 @@ mp_obj_t str_format(uint n_args, const mp_obj_t *args) {
         }
     }
 
-    return mp_obj_new_str(qstr_from_str_take(vstr->buf, vstr->alloc));
+    return mp_obj_new_str(qstr_from_strn_take(vstr->buf, vstr->alloc, vstr->len));
 }
 
 static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj, 2, 4, str_find);
@@ -339,7 +353,7 @@ mp_obj_t str_it_iternext(mp_obj_t self_in) {
     mp_obj_str_it_t *self = self_in;
     const char *str = qstr_str(self->str->qstr);
     if (self->cur < strlen(str)) {
-        mp_obj_t o_out = mp_obj_new_str(qstr_from_strn_copy(str + self->cur, 1));
+        mp_obj_t o_out = mp_obj_new_str(qstr_from_strn(str + self->cur, 1));
         self->cur += 1;
         return o_out;
     } else {
diff --git a/py/objtuple.c b/py/objtuple.c
index fd6d415e289d393f4a2f6ad22401bdf02f47861a..ec35ef8550f254a0a305e1dea69a892df92bdeb5 100644
--- a/py/objtuple.c
+++ b/py/objtuple.c
@@ -5,7 +5,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "runtime.h"
diff --git a/py/objtype.c b/py/objtype.c
index d448bb03edb512b1588d68caf397889686a4a69c..5dea6938d7315203dd6c1ca5fa7eacc35cd7b3d7 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "map.h"
 #include "runtime0.h"
@@ -166,7 +166,7 @@ static mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     if (op_name == NULL) {
         return MP_OBJ_NULL;
     }
-    mp_obj_t member = mp_obj_class_lookup(lhs->base.type, qstr_from_str_static(op_name));
+    mp_obj_t member = mp_obj_class_lookup(lhs->base.type, QSTR_FROM_STR_STATIC(op_name));
     if (member != MP_OBJ_NULL) {
         return rt_call_function_2(member, lhs_in, rhs_in);
     } else {
@@ -219,7 +219,7 @@ static bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
 
 bool class_store_item(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
     mp_obj_class_t *self = self_in;
-    mp_obj_t member = mp_obj_class_lookup(self->base.type, qstr_from_str_static("__setitem__"));
+    mp_obj_t member = mp_obj_class_lookup(self->base.type, QSTR_FROM_STR_STATIC("__setitem__"));
     if (member != MP_OBJ_NULL) {
         mp_obj_t args[3] = {self_in, index, value};
         rt_call_function_n_kw(member, 3, 0, args);
diff --git a/py/objzip.c b/py/objzip.c
index 72db06ac20c104e72d96bd559a258a84eee4461b..6560a08bc744d57622763c08f19135130dc72b75 100644
--- a/py/objzip.c
+++ b/py/objzip.c
@@ -3,6 +3,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 
diff --git a/py/parse.c b/py/parse.c
index 93d75424acd6ef882bb3ae7a595d7ecb5c44728b..4288c74ccf3bb25ef435a9ee4d8cdd05cc4f52ee 100644
--- a/py/parse.c
+++ b/py/parse.c
@@ -8,7 +8,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "parse.h"
 
@@ -205,7 +205,7 @@ static void push_result_token(parser_t *parser, const mp_lexer_t *lex) {
     const mp_token_t *tok = mp_lexer_cur(lex);
     mp_parse_node_t pn;
     if (tok->kind == MP_TOKEN_NAME) {
-        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, qstr_from_strn_copy(tok->str, tok->len));
+        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, qstr_from_strn(tok->str, tok->len));
     } else if (tok->kind == MP_TOKEN_NUMBER) {
         bool dec = false;
         bool small_int = true;
@@ -254,16 +254,16 @@ static void push_result_token(parser_t *parser, const mp_lexer_t *lex) {
             }
         }
         if (dec) {
-            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_DECIMAL, qstr_from_strn_copy(str, len));
+            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_DECIMAL, qstr_from_strn(str, len));
         } else if (small_int && !overflow && MP_FIT_SMALL_INT(int_val)) {
             pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, int_val);
         } else {
-            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_INTEGER, qstr_from_strn_copy(str, len));
+            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_INTEGER, qstr_from_strn(str, len));
         }
     } else if (tok->kind == MP_TOKEN_STRING) {
-        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_STRING, qstr_from_strn_copy(tok->str, tok->len));
+        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_STRING, qstr_from_strn(tok->str, tok->len));
     } else if (tok->kind == MP_TOKEN_BYTES) {
-        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_BYTES, qstr_from_strn_copy(tok->str, tok->len));
+        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_BYTES, qstr_from_strn(tok->str, tok->len));
     } else {
         pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, tok->kind);
     }
diff --git a/py/py.mk b/py/py.mk
index ce5169f7774c242dfeccc92e7f70187cd2cacfd8..72013ef9994ad85c2af5b936197754ece34c6af0 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -25,13 +25,8 @@ endif
 
 # default settings; can be overriden in main Makefile
 
-ifndef PY_SRC
-PY_SRC = ../py
-endif
-
-ifndef BUILD
-BUILD = build
-endif
+PY_SRC ?= ../py
+BUILD ?= build
 
 # to create the build directory
 
@@ -42,6 +37,10 @@ $(BUILD):
 
 PY_BUILD = $(BUILD)/py.
 
+# file containing qstr defs for the core Python bit
+
+PY_QSTR_DEFS = $(PY_SRC)/qstrdefs.h
+
 # py object files
 
 PY_O_BASENAME = \
@@ -97,6 +96,7 @@ PY_O_BASENAME = \
 	objstr.o \
 	objtuple.o \
 	objtype.o \
+	objzip.o \
 	stream.o \
 	builtin.o \
 	builtinimport.o \
@@ -105,12 +105,21 @@ PY_O_BASENAME = \
 	vm.o \
 	showbc.o \
 	repl.o \
-	objzip.o \
 
 # prepend the build destination prefix to the py object files
 
 PY_O = $(addprefix $(PY_BUILD), $(PY_O_BASENAME))
 
+# qstr data
+
+$(PY_BUILD)qstr.o: $(PY_BUILD)qstrdefs.generated.h
+
+$(PY_BUILD)qstrdefs.generated.h: $(PY_QSTR_DEFS) $(QSTR_DEFS) $(PY_SRC)/makeqstrdata.py
+	$(ECHO) "makeqstrdata $(PY_QSTR_DEFS) $(QSTR_DEFS)"
+	$(Q)python $(PY_SRC)/makeqstrdata.py $(PY_QSTR_DEFS) $(QSTR_DEFS) > $@
+
+# emitters
+
 $(PY_BUILD)emitnx64.o: $(PY_SRC)/emitnative.c $(PY_SRC)/emit.h mpconfigport.h
 	$(ECHO) "CC $<"
 	$(Q)$(CC) $(CFLAGS) -DN_X64 -c -o $@ $<
@@ -119,11 +128,13 @@ $(PY_BUILD)emitnthumb.o: $(PY_SRC)/emitnative.c $(PY_SRC)/emit.h mpconfigport.h
 	$(ECHO) "CC $<"
 	$(Q)$(CC) $(CFLAGS) -DN_THUMB -c -o $@ $<
 
+# general source files
+
 $(PY_BUILD)%.o: $(PY_SRC)/%.S
 	$(ECHO) "CC $<"
 	$(Q)$(CC) $(CFLAGS) -c -o $@ $<
 
-$(PY_BUILD)%.o: $(PY_SRC)/%.c mpconfigport.h
+$(PY_BUILD)%.o: $(PY_SRC)/%.c mpconfigport.h $(PY_SRC)/qstr.h $(PY_QSTR_DEFS) $(QSTR_DEFS)
 	$(ECHO) "CC $<"
 	$(Q)$(CC) $(CFLAGS) -c -o $@ $<
 
@@ -141,5 +152,5 @@ $(PY_BUILD)vm.o: $(PY_SRC)/vm.c
 
 $(PY_BUILD)parse.o: $(PY_SRC)/grammar.h
 $(PY_BUILD)compile.o: $(PY_SRC)/grammar.h
-$(PY_BUILD)/emitcpy.o: $(PY_SRC)/emit.h
+$(PY_BUILD)emitcpy.o: $(PY_SRC)/emit.h
 $(PY_BUILD)emitbc.o: $(PY_SRC)/emit.h
diff --git a/py/qstr.c b/py/qstr.c
index 93ae3ab665ef471100f6d40e8b6c4aad1069e2fe..2830341a2dc2f568dcd5737ef5725907d2c0ea27 100644
--- a/py/qstr.c
+++ b/py/qstr.c
@@ -2,7 +2,8 @@
 #include <string.h>
 
 #include "misc.h"
-#include "mpqstr.h"
+#include "mpconfig.h"
+#include "qstr.h"
 
 // NOTE: we are using linear arrays to store and search for qstr's (unique strings, interned strings)
 // ultimately we will replace this with a static hash table of some kind
@@ -15,12 +16,33 @@
 #define DEBUG_printf(args...) (void)0
 #endif
 
+// A qstr is an index into the qstr pool.
+// The data for a qstr contains (hash, length, data).
+// For now we use very simple encoding, just to get the framework correct:
+//  - hash is 2 bytes (simply the sum of data bytes)
+//  - length is 2 bytes
+//  - data follows
+//  - \0 terminated (for now, so they can be printed using printf)
+
+#define Q_GET_HASH(q)   ((q)[0] | ((q)[1] << 8))
+#define Q_GET_ALLOC(q)  (4 + Q_GET_LENGTH(q) + 1)
+#define Q_GET_LENGTH(q) ((q)[2] | ((q)[3] << 8))
+#define Q_GET_DATA(q)   ((q) + 4)
+
+static machine_uint_t compute_hash(const byte *data, uint len) {
+    machine_uint_t hash = 0;
+    for (const byte *top = data + len; data < top; data++) {
+        hash += *data;
+    }
+    return hash & 0xffff;
+}
+
 typedef struct _qstr_pool_t {
     struct _qstr_pool_t *prev;
     uint total_prev_len;
     uint alloc;
     uint len;
-    const char *qstrs[];
+    const byte *qstrs[];
 } qstr_pool_t;
 
 const static qstr_pool_t const_pool = {
@@ -29,9 +51,11 @@ const static qstr_pool_t const_pool = {
     10,                 // set so that the first dynamically allocated pool is twice this size; must be <= the len (just below)
     MP_QSTR_number_of,  // corresponds to number of strings in array just below
     {
-        "nil", // must be first, since 0 qstr is nil
-#define Q(id) #id,
-#include "mpqstrraw.h"
+        (const byte*) "\0\0\0\0", // invalid/no qstr has empty data
+        (const byte*) "\0\0\0\0", // empty qstr
+#define Q(id, str) str,
+// TODO having 'build/' here is a bit of a hack, should take config variable from Makefile
+#include "build/py.qstrdefs.generated.h"
 #undef Q
     },
 };
@@ -42,8 +66,20 @@ void qstr_init(void) {
     last_pool = (qstr_pool_t*)&const_pool; // we won't modify the const_pool since it has no allocated room left
 }
 
-static qstr qstr_add(const char *str) {
-    DEBUG_printf("QSTR: add %s\n", str);
+static const byte *find_qstr(qstr q) {
+    // search pool for this qstr
+    for (qstr_pool_t *pool = last_pool; pool != NULL; pool = pool->prev) {
+        if (q >= pool->total_prev_len) {
+            return pool->qstrs[q - pool->total_prev_len];
+        }
+    }
+
+    // not found
+    return 0;
+}
+
+static qstr qstr_add(const byte *q_ptr) {
+    DEBUG_printf("QSTR: add hash=%d len=%d data=%.*s\n", Q_GET_HASH(q_ptr), Q_GET_LENGTH(q_ptr), Q_GET_LENGTH(q_ptr), Q_GET_DATA(q_ptr));
 
     // make sure we have room in the pool for a new qstr
     if (last_pool->len >= last_pool->alloc) {
@@ -57,55 +93,95 @@ static qstr qstr_add(const char *str) {
     }
 
     // add the new qstr
-    last_pool->qstrs[last_pool->len++] = str;
+    last_pool->qstrs[last_pool->len++] = q_ptr;
 
     // return id for the newly-added qstr
     return last_pool->total_prev_len + last_pool->len - 1;
 }
 
-qstr qstr_from_str_static(const char *str) {
+static qstr qstr_find_strn(const byte *str, uint str_len) {
+    // work out hash of str
+    machine_uint_t str_hash = compute_hash((const byte*)str, str_len);
+
+    // search pools for the data
     for (qstr_pool_t *pool = last_pool; pool != NULL; pool = pool->prev) {
-        for (const char **qstr = pool->qstrs, **qstr_top = pool->qstrs + pool->len; qstr < qstr_top; qstr++) {
-            if (strcmp(*qstr, str) == 0) {
-                return pool->total_prev_len + (qstr - pool->qstrs);
+        for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) {
+            if (Q_GET_HASH(*q) == str_hash && Q_GET_LENGTH(*q) == str_len && strncmp((const char*)Q_GET_DATA(*q), (const char*)str, str_len) == 0) {
+                return pool->total_prev_len + (q - pool->qstrs);
             }
         }
     }
-    return qstr_add(str);
+
+    // not found; return null qstr
+    return 0;
 }
 
-qstr qstr_from_str_take(char *str, int alloc_len) {
-    for (qstr_pool_t *pool = last_pool; pool != NULL; pool = pool->prev) {
-        for (const char **qstr = pool->qstrs, **qstr_top = pool->qstrs + pool->len; qstr < qstr_top; qstr++) {
-            if (strcmp(*qstr, str) == 0) {
-                m_del(char, str, alloc_len);
-                return pool->total_prev_len + (qstr - pool->qstrs);
-            }
-        }
-    }
-    return qstr_add(str);
+qstr qstr_from_str(const char *str) {
+    return qstr_from_strn(str, strlen(str));
 }
 
-qstr qstr_from_strn_copy(const char *str, int len) {
-    for (qstr_pool_t *pool = last_pool; pool != NULL; pool = pool->prev) {
-        for (const char **qstr = pool->qstrs, **qstr_top = pool->qstrs + pool->len; qstr < qstr_top; qstr++) {
-            if (strncmp(*qstr, str, len) == 0 && (*qstr)[len] == '\0') {
-                return pool->total_prev_len + (qstr - pool->qstrs);
-            }
-        }
+qstr qstr_from_strn(const char *str, uint len) {
+    qstr q = qstr_find_strn((const byte*)str, len);
+    if (q == 0) {
+        machine_uint_t hash = compute_hash((const byte*)str, len);
+        byte *q_ptr = m_new(byte, 4 + len + 1);
+        q_ptr[0] = hash;
+        q_ptr[1] = hash >> 8;
+        q_ptr[2] = len;
+        q_ptr[3] = len >> 8;
+        memcpy(q_ptr + 4, str, len);
+        q_ptr[4 + len] = '\0';
+        q = qstr_add(q_ptr);
     }
-    return qstr_add(strndup(str, len));
+    return q;
 }
 
-// convert qstr id to pointer to its string
-const char *qstr_str(qstr qstr) {
-    // search
-    for (qstr_pool_t *pool = last_pool; pool != NULL; pool = pool->prev) {
-        if (qstr >= pool->total_prev_len) {
-            return pool->qstrs[qstr - pool->total_prev_len];
-        }
+qstr qstr_from_strn_take(char *str, uint alloc_len, uint len) {
+    qstr q = qstr_from_strn(str, len);
+    m_del(char, str, alloc_len);
+    return q;
+}
+
+byte *qstr_build_start(uint len, byte **q_ptr) {
+    assert(len <= 65535);
+    *q_ptr = m_new(byte, 4 + len + 1);
+    (*q_ptr)[2] = len;
+    (*q_ptr)[3] = len >> 8;
+    return Q_GET_DATA(*q_ptr);
+}
+
+qstr qstr_build_end(byte *q_ptr) {
+    qstr q = qstr_find_strn(Q_GET_DATA(q_ptr), Q_GET_LENGTH(q_ptr));
+    if (q == 0) {
+        machine_uint_t len = Q_GET_LENGTH(q_ptr);
+        machine_uint_t hash = compute_hash(Q_GET_DATA(q_ptr), len);
+        q_ptr[0] = hash;
+        q_ptr[1] = hash >> 8;
+        q_ptr[4 + len] = '\0';
+        q = qstr_add(q_ptr);
+    } else {
+        m_del(byte, q_ptr, Q_GET_ALLOC(q_ptr));
     }
+    return q;
+}
+
+machine_uint_t qstr_hash(qstr q) {
+    return Q_GET_HASH(find_qstr(q));
+}
+
+uint qstr_len(qstr q) {
+    const byte *qd = find_qstr(q);
+    return Q_GET_LENGTH(qd);
+}
+
+// XXX to remove!
+const char *qstr_str(qstr q) {
+    const byte *qd = find_qstr(q);
+    return (const char*)Q_GET_DATA(qd);
+}
 
-    // not found, return nil
-    return const_pool.qstrs[0];
+const byte *qstr_data(qstr q, uint *len) {
+    const byte *qd = find_qstr(q);
+    *len = Q_GET_LENGTH(qd);
+    return Q_GET_DATA(qd);
 }
diff --git a/py/qstr.h b/py/qstr.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c331c34af40106cee35bbc6b6a83d20d90a887e
--- /dev/null
+++ b/py/qstr.h
@@ -0,0 +1,35 @@
+// See qstrraw.h for a list of qstr's that are available as constants.
+// Reference them as MP_QSTR_xxxx.
+//
+// Note: it would be possible to define MP_QSTR_xxx as qstr_from_str_static("xxx")
+// for qstrs that are referenced this way, but you don't want to have them in ROM.
+
+enum {
+    MP_QSTR_NULL = 0, // indicates invalid/no qstr
+    MP_QSTR_ = 1, // the empty qstr
+#define Q(id, str) MP_QSTR_##id,
+// TODO having 'build/py.' here is a bit of a hack, should take config variable from Makefile
+#include "build/py.qstrdefs.generated.h"
+#undef Q
+    MP_QSTR_number_of,
+} category_t;
+
+typedef machine_uint_t qstr;
+
+#define QSTR_FROM_STR_STATIC(s) (qstr_from_strn((s), strlen(s)))
+
+void qstr_init(void);
+
+qstr qstr_from_str(const char *str);
+qstr qstr_from_strn(const char *str, uint len);
+//qstr qstr_from_str_static(const char *str);
+qstr qstr_from_strn_take(char *str, uint alloc_len, uint len);
+//qstr qstr_from_strn_copy(const char *str, int len);
+
+byte* qstr_build_start(uint len, byte **q_ptr);
+qstr qstr_build_end(byte *q_ptr);
+
+machine_uint_t qstr_hash(qstr q);
+const char* qstr_str(qstr q);
+uint qstr_len(qstr q);
+const byte* qstr_data(qstr q, uint *len);
diff --git a/py/mpqstrraw.h b/py/qstrdefs.h
similarity index 100%
rename from py/mpqstrraw.h
rename to py/qstrdefs.h
diff --git a/py/runtime.c b/py/runtime.c
index d8fc3ff6e40eb39f91ed343d4c34ccb363725d0a..210047ac0a81b1e3c4828dd3a1e398d77630d43a 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -11,7 +11,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime0.h"
 #include "runtime.h"
@@ -154,7 +154,7 @@ void rt_init(void) {
 
 #if MICROPY_CPYTHON_COMPAT
     // Precreate sys module, so "import sys" didn't throw exceptions.
-    mp_obj_new_module(qstr_from_str_static("sys"));
+    mp_obj_new_module(QSTR_FROM_STR_STATIC("sys"));
 #endif
 
     mp_module_micropython_init();
diff --git a/py/scope.c b/py/scope.c
index 5cc0bda068587393f3a42bd4f9edca02109749bd..1d240bb63e33a57287d71dc83917db860828d694 100644
--- a/py/scope.c
+++ b/py/scope.c
@@ -5,6 +5,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "parse.h"
 #include "scope.h"
 
@@ -17,7 +18,7 @@ scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, uint
     scope->source_file = source_file;
     switch (kind) {
         case SCOPE_MODULE:
-            scope->simple_name = qstr_from_str_static("<module>");
+            scope->simple_name = QSTR_FROM_STR_STATIC("<module>");
             break;
         case SCOPE_FUNCTION:
         case SCOPE_CLASS:
@@ -25,19 +26,19 @@ scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, uint
             scope->simple_name = MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pn)->nodes[0]);
             break;
         case SCOPE_LAMBDA:
-            scope->simple_name = qstr_from_str_static("<lambda>");
+            scope->simple_name = QSTR_FROM_STR_STATIC("<lambda>");
             break;
         case SCOPE_LIST_COMP:
-            scope->simple_name = qstr_from_str_static("<listcomp>");
+            scope->simple_name = QSTR_FROM_STR_STATIC("<listcomp>");
             break;
         case SCOPE_DICT_COMP:
-            scope->simple_name = qstr_from_str_static("<dictcomp>");
+            scope->simple_name = QSTR_FROM_STR_STATIC("<dictcomp>");
             break;
         case SCOPE_SET_COMP:
-            scope->simple_name = qstr_from_str_static("<setcomp>");
+            scope->simple_name = QSTR_FROM_STR_STATIC("<setcomp>");
             break;
         case SCOPE_GEN_EXPR:
-            scope->simple_name = qstr_from_str_static("<genexpr>");
+            scope->simple_name = QSTR_FROM_STR_STATIC("<genexpr>");
             break;
         default:
             assert(0);
diff --git a/py/showbc.c b/py/showbc.c
index f34449ed10da6b29e6b4e6aeee9380dc070dea6f..f914223933cc244e71f262c9c75b95860175b9c3 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -6,6 +6,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "bc0.h"
 
 #if MICROPY_DEBUG_PRINTERS
diff --git a/py/stream.c b/py/stream.c
index d3a11affbc87411cbd0cb8ad5591f5cfb1b05d7d..88ddc5e6c9a35aad8cc12159e72c8d286e641d64 100644
--- a/py/stream.c
+++ b/py/stream.c
@@ -3,7 +3,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "stream.h"
 
@@ -23,15 +23,14 @@ static mp_obj_t stream_read(uint n_args, const mp_obj_t *args) {
     if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) {
         return stream_readall(args[0]);
     }
-    // +1 because so far we mark end of string with \0
-    char *buf = m_new(char, sz + 1);
+    char *buf = m_new(char, sz);
     int error;
     machine_int_t out_sz = o->type->stream_p.read(o, buf, sz, &error);
     if (out_sz == -1) {
         nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[Errno %d]", error));
     } else {
-        buf[out_sz] = 0;
-        return mp_obj_new_str(qstr_from_str_take(buf, /*out_sz,*/ sz + 1));
+        // TODO don't intern this string
+        return mp_obj_new_str(qstr_from_strn_take(buf, sz, out_sz));
     }
 }
 
@@ -42,8 +41,8 @@ static mp_obj_t stream_write(mp_obj_t self_in, mp_obj_t arg) {
         nlr_jump(mp_obj_new_exception_msg(MP_QSTR_OSError, "Operation not supported"));
     }
 
-    const char *buf = qstr_str(mp_obj_get_qstr(arg));
-    machine_int_t sz = strlen(buf);
+    uint sz;
+    const byte *buf = qstr_data(mp_obj_get_qstr(arg), &sz);
     int error;
     machine_int_t out_sz = o->type->stream_p.write(self_in, buf, sz, &error);
     if (out_sz == -1) {
@@ -92,10 +91,9 @@ static mp_obj_t stream_readall(mp_obj_t self_in) {
             }
         }
     }
-    vstr_set_size(vstr, total_size + 1); // TODO: for \0
-    buf = vstr_str(vstr);
-    buf[total_size] = 0;
-    return mp_obj_new_str(qstr_from_str_take(buf, total_size + 1));
+    // TODO don't intern this string
+    vstr_set_size(vstr, total_size);
+    return mp_obj_new_str(qstr_from_strn_take(vstr->buf, vstr->alloc, total_size));
 }
 
 // Unbuffered, inefficient implementation of readline() for raw I/O files.
@@ -113,7 +111,7 @@ static mp_obj_t stream_unbuffered_readline(uint n_args, const mp_obj_t *args) {
 
     vstr_t *vstr;
     if (max_size != -1) {
-        vstr = vstr_new_size(max_size + 1); // TODO: \0
+        vstr = vstr_new_size(max_size);
     } else {
         vstr = vstr_new();
     }
@@ -134,10 +132,9 @@ static mp_obj_t stream_unbuffered_readline(uint n_args, const mp_obj_t *args) {
             break;
         }
     }
-    // TODO: \0
-    vstr_add_byte(vstr, 0);
+    // TODO don't intern this string
     vstr_shrink(vstr);
-    return mp_obj_new_str(qstr_from_str_take(vstr_str(vstr), vstr_len(vstr)));
+    return mp_obj_new_str(qstr_from_strn_take(vstr_str(vstr), vstr->alloc, vstr_len(vstr)));
 }
 
 
diff --git a/py/strtonum.c b/py/strtonum.c
index 48a746603fce1346054606e49482ed0b61910b06..d8bb05ac8d955e38c1063634ba0556b3035281fd 100644
--- a/py/strtonum.c
+++ b/py/strtonum.c
@@ -6,7 +6,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "nlr.h"
 #include "obj.h"
 
diff --git a/py/vm.c b/py/vm.c
index 75c21d1b3c4af76240bfcd9cfd68b01efaee1fbd..c41146ac8ffe5ad600b97874a63e1b5593f17f39 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -7,6 +7,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
 #include "bc0.h"
diff --git a/stm/Makefile b/stm/Makefile
index 3ea1472dc6e87e21b57c7f80e3e8abc3aed9e214..9c0fe2acf060ba658ea80360799a25c49e71cce6 100644
--- a/stm/Makefile
+++ b/stm/Makefile
@@ -1,6 +1,9 @@
 # define main target
 all: all2
 
+# qstr definitions (must come before including py.mk)
+QSTR_DEFS = qstrdefsport.h
+
 # include py core make definitions
 include ../py/py.mk
 
@@ -131,7 +134,7 @@ SRC_CC3K = \
 	ccspi.c \
 	pybcc3k.c \
 
-OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o) $(SRC_FATFS:.c=.o) $(SRC_STM:.c=.o) $(SRC_CC3K:.c=.o)) $(PY_O)
+OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o) $(SRC_FATFS:.c=.o) $(SRC_STM:.c=.o) $(SRC_CC3K:.c=.o))
 #OBJ += $(addprefix $(BUILD)/, $(SRC_STM_OTG:.c=.o))
 
 all2: $(BUILD) $(BUILD)/flash.dfu
@@ -155,7 +158,7 @@ $(BUILD)/%.o: %.s
 	$(ECHO) "AS $<"
 	$(Q)$(AS) -o $@ $<
 
-$(BUILD)/%.o: %.c
+$(BUILD)/%.o: %.c $(QSTR_DEFS)
 	$(ECHO) "CC $<"
 	$(Q)$(CC) $(CFLAGS) -c -o $@ $<
 
diff --git a/stm/adc.c b/stm/adc.c
index ecbcd6bc0cb6063046e178db21e0df4f47e9ecca..e54a464a843f82a5658e5245db95fc10f273952c 100644
--- a/stm/adc.c
+++ b/stm/adc.c
@@ -3,6 +3,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "adc.h"
 
diff --git a/stm/audio.c b/stm/audio.c
index e2aa32b9fceda2b9d47adb81344532e7ae97fd79..c1a0c847473ba7f06c72b53fdea07f95871a4d83 100644
--- a/stm/audio.c
+++ b/stm/audio.c
@@ -1,4 +1,5 @@
 #include <stdint.h>
+#include <string.h>
 
 #include "stm32f4xx_rcc.h"
 #include "stm32f4xx_gpio.h"
@@ -7,6 +8,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "parse.h"
 #include "obj.h"
 #include "runtime.h"
@@ -90,9 +92,9 @@ void audio_init(void) {
     // enable interrupt
 
     // Python interface
-    mp_obj_t m = mp_obj_new_module(qstr_from_str_static("audio"));
-    rt_store_attr(m, qstr_from_str_static("dac"), rt_make_function_n(1, pyb_audio_dac));
-    rt_store_attr(m, qstr_from_str_static("is_full"), rt_make_function_n(0, pyb_audio_is_full));
-    rt_store_attr(m, qstr_from_str_static("fill"), rt_make_function_n(1, pyb_audio_fill));
-    rt_store_name(qstr_from_str_static("audio"), m);
+    mp_obj_t m = mp_obj_new_module(QSTR_FROM_STR_STATIC("audio"));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("dac"), rt_make_function_n(1, pyb_audio_dac));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("is_full"), rt_make_function_n(0, pyb_audio_is_full));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("fill"), rt_make_function_n(1, pyb_audio_fill));
+    rt_store_name(QSTR_FROM_STR_STATIC("audio"), m);
 }
diff --git a/stm/i2c.c b/stm/i2c.c
index 8503f26647ad44db1fd55aa27218a56e8598d839..1dfd74a8d9dee27cca7eb3664f822cd5125f7b62 100644
--- a/stm/i2c.c
+++ b/stm/i2c.c
@@ -5,6 +5,7 @@
 #include "misc.h"
 #include "systick.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 
 typedef enum {
diff --git a/stm/lcd.c b/stm/lcd.c
index 82e42b779d30bd85b5f69256f75c516b5cf3e1fa..56f0ffe64c17f908030b0c891679e90c5d6a025a 100644
--- a/stm/lcd.c
+++ b/stm/lcd.c
@@ -4,6 +4,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "parse.h"
 #include "obj.h"
 #include "runtime.h"
@@ -219,15 +220,15 @@ void lcd_init(void) {
     lcd_next_line = 0;
 
     // Python interface
-    mp_obj_t m = mp_obj_new_module(qstr_from_str_static("lcd"));
-    rt_store_attr(m, qstr_from_str_static("lcd8"), rt_make_function_n(2, lcd_draw_pixel_8));
-    rt_store_attr(m, qstr_from_str_static("clear"), rt_make_function_n(0, lcd_pix_clear));
-    rt_store_attr(m, qstr_from_str_static("get"), rt_make_function_n(2, lcd_pix_get));
-    rt_store_attr(m, qstr_from_str_static("set"), rt_make_function_n(2, lcd_pix_set));
-    rt_store_attr(m, qstr_from_str_static("reset"), rt_make_function_n(2, lcd_pix_reset));
-    rt_store_attr(m, qstr_from_str_static("show"), rt_make_function_n(0, lcd_pix_show));
-    rt_store_attr(m, qstr_from_str_static("text"), rt_make_function_n(1, lcd_print));
-    rt_store_name(qstr_from_str_static("lcd"), m);
+    mp_obj_t m = mp_obj_new_module(QSTR_FROM_STR_STATIC("lcd"));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("lcd8"), rt_make_function_n(2, lcd_draw_pixel_8));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("clear"), rt_make_function_n(0, lcd_pix_clear));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("get"), rt_make_function_n(2, lcd_pix_get));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("set"), rt_make_function_n(2, lcd_pix_set));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("reset"), rt_make_function_n(2, lcd_pix_reset));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("show"), rt_make_function_n(0, lcd_pix_show));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("text"), rt_make_function_n(1, lcd_print));
+    rt_store_name(QSTR_FROM_STR_STATIC("lcd"), m);
 }
 
 void lcd_print_str(const char *str) {
diff --git a/stm/led.c b/stm/led.c
index 8248a5c7d5fb939b805182349712d7232a1392e6..f471973875191de6d8da33400ec10665624378ef 100644
--- a/stm/led.c
+++ b/stm/led.c
@@ -4,6 +4,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "led.h"
 
diff --git a/stm/lexerfatfs.c b/stm/lexerfatfs.c
index 461fbb3709ecdd02e8a0c568c56547fcd11355a2..bf82349f35e147c7ada6f9bd4fc3b85fea14e8c6 100644
--- a/stm/lexerfatfs.c
+++ b/stm/lexerfatfs.c
@@ -4,6 +4,8 @@
 #include "ff.h"
 
 #include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "lexerfatfs.h"
 
diff --git a/stm/main.c b/stm/main.c
index 7c92e8e18089681193308bd39437133b4030b91f..a3ad8caf3dc3a499f317542de27ecad18a3f94eb 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <string.h>
 #include <stm32f4xx.h>
 #include <stm32f4xx_rcc.h>
 #include <stm32f4xx_syscfg.h>
@@ -15,7 +16,7 @@
 #include "misc.h"
 #include "ff.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "nlr.h"
 #include "misc.h"
 #include "lexer.h"
@@ -677,11 +678,10 @@ void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, m
 mp_obj_t file_obj_read(mp_obj_t self_in, mp_obj_t arg) {
     pyb_file_obj_t *self = self_in;
     int n = mp_obj_get_int(arg);
-    char *buf = m_new(char, n + 1);
+    char *buf = m_new(char, n);
     UINT n_out;
     f_read(&self->fp, buf, n, &n_out);
-    buf[n_out] = 0;
-    return mp_obj_new_str(qstr_from_str_take(buf, n + 1));
+    return mp_obj_new_str(qstr_from_strn_take(buf, n, n_out));
 }
 
 mp_obj_t file_obj_write(mp_obj_t self_in, mp_obj_t arg) {
@@ -827,37 +827,37 @@ soft_reset:
 
     // add some functions to the python namespace
     {
-        rt_store_name(qstr_from_str_static("help"), rt_make_function_n(0, pyb_help));
-
-        mp_obj_t m = mp_obj_new_module(qstr_from_str_static("pyb"));
-        rt_store_attr(m, qstr_from_str_static("info"), rt_make_function_n(0, pyb_info));
-        rt_store_attr(m, qstr_from_str_static("sd_test"), rt_make_function_n(0, pyb_sd_test));
-        rt_store_attr(m, qstr_from_str_static("stop"), rt_make_function_n(0, pyb_stop));
-        rt_store_attr(m, qstr_from_str_static("standby"), rt_make_function_n(0, pyb_standby));
-        rt_store_attr(m, qstr_from_str_static("source_dir"), rt_make_function_n(1, pyb_source_dir));
-        rt_store_attr(m, qstr_from_str_static("main"), rt_make_function_n(1, pyb_main));
-        rt_store_attr(m, qstr_from_str_static("sync"), rt_make_function_n(0, pyb_sync));
-        rt_store_attr(m, qstr_from_str_static("gc"), rt_make_function_n(0, pyb_gc));
-        rt_store_attr(m, qstr_from_str_static("delay"), rt_make_function_n(1, pyb_delay));
-        rt_store_attr(m, qstr_from_str_static("led"), rt_make_function_n(1, pyb_led));
-        rt_store_attr(m, qstr_from_str_static("switch"), (mp_obj_t)&pyb_switch_obj);
-        rt_store_attr(m, qstr_from_str_static("servo"), rt_make_function_n(2, pyb_servo_set));
-        rt_store_attr(m, qstr_from_str_static("pwm"), rt_make_function_n(2, pyb_pwm_set));
-        rt_store_attr(m, qstr_from_str_static("accel"), (mp_obj_t)&pyb_mma_read_obj);
-        rt_store_attr(m, qstr_from_str_static("mma_read"), (mp_obj_t)&pyb_mma_read_all_obj);
-        rt_store_attr(m, qstr_from_str_static("mma_mode"), (mp_obj_t)&pyb_mma_write_mode_obj);
-        rt_store_attr(m, qstr_from_str_static("hid"), rt_make_function_n(1, pyb_hid_send_report));
-        rt_store_attr(m, qstr_from_str_static("time"), rt_make_function_n(0, pyb_rtc_read));
-        rt_store_attr(m, qstr_from_str_static("rand"), rt_make_function_n(0, pyb_rng_get));
-        rt_store_attr(m, qstr_from_str_static("Led"), rt_make_function_n(1, pyb_Led));
-        rt_store_attr(m, qstr_from_str_static("Servo"), rt_make_function_n(1, pyb_Servo));
-        rt_store_attr(m, qstr_from_str_static("I2C"), rt_make_function_n(2, pyb_I2C));
-        rt_store_attr(m, qstr_from_str_static("gpio"), (mp_obj_t)&pyb_gpio_obj);
-        rt_store_attr(m, qstr_from_str_static("Usart"), rt_make_function_n(2, pyb_Usart));
-        rt_store_attr(m, qstr_from_str_static("ADC"), rt_make_function_n(1, pyb_ADC));
-        rt_store_name(qstr_from_str_static("pyb"), m);
-
-        rt_store_name(qstr_from_str_static("open"), rt_make_function_n(2, pyb_io_open));
+        rt_store_name(MP_QSTR_help, rt_make_function_n(0, pyb_help));
+
+        mp_obj_t m = mp_obj_new_module(MP_QSTR_pyb);
+        rt_store_attr(m, MP_QSTR_info, rt_make_function_n(0, pyb_info));
+        rt_store_attr(m, MP_QSTR_sd_test, rt_make_function_n(0, pyb_sd_test));
+        rt_store_attr(m, MP_QSTR_stop, rt_make_function_n(0, pyb_stop));
+        rt_store_attr(m, MP_QSTR_standby, rt_make_function_n(0, pyb_standby));
+        rt_store_attr(m, MP_QSTR_source_dir, rt_make_function_n(1, pyb_source_dir));
+        rt_store_attr(m, MP_QSTR_main, rt_make_function_n(1, pyb_main));
+        rt_store_attr(m, MP_QSTR_sync, rt_make_function_n(0, pyb_sync));
+        rt_store_attr(m, MP_QSTR_gc, rt_make_function_n(0, pyb_gc));
+        rt_store_attr(m, MP_QSTR_delay, rt_make_function_n(1, pyb_delay));
+        rt_store_attr(m, MP_QSTR_led, rt_make_function_n(1, pyb_led));
+        rt_store_attr(m, MP_QSTR_switch, (mp_obj_t)&pyb_switch_obj);
+        rt_store_attr(m, MP_QSTR_servo, rt_make_function_n(2, pyb_servo_set));
+        rt_store_attr(m, MP_QSTR_pwm, rt_make_function_n(2, pyb_pwm_set));
+        rt_store_attr(m, MP_QSTR_accel, (mp_obj_t)&pyb_mma_read_obj);
+        rt_store_attr(m, MP_QSTR_mma_read, (mp_obj_t)&pyb_mma_read_all_obj);
+        rt_store_attr(m, MP_QSTR_mma_mode, (mp_obj_t)&pyb_mma_write_mode_obj);
+        rt_store_attr(m, MP_QSTR_hid, rt_make_function_n(1, pyb_hid_send_report));
+        rt_store_attr(m, MP_QSTR_time, rt_make_function_n(0, pyb_rtc_read));
+        rt_store_attr(m, MP_QSTR_rand, rt_make_function_n(0, pyb_rng_get));
+        rt_store_attr(m, MP_QSTR_Led, rt_make_function_n(1, pyb_Led));
+        rt_store_attr(m, MP_QSTR_Servo, rt_make_function_n(1, pyb_Servo));
+        rt_store_attr(m, MP_QSTR_I2C, rt_make_function_n(2, pyb_I2C));
+        rt_store_attr(m, MP_QSTR_gpio, (mp_obj_t)&pyb_gpio_obj);
+        rt_store_attr(m, MP_QSTR_Usart, rt_make_function_n(2, pyb_Usart));
+        rt_store_attr(m, MP_QSTR_ADC, rt_make_function_n(1, pyb_ADC));
+        rt_store_name(MP_QSTR_pyb, m);
+
+        rt_store_name(MP_QSTR_open, rt_make_function_n(2, pyb_io_open));
     }
 
     // print a message to the LCD
diff --git a/stm/mma.c b/stm/mma.c
index f5409ae1202f9b2275b29624d67657f6b3d86b70..a32b0ddebe718e4301d60f0071eea920fd6cbc4f 100644
--- a/stm/mma.c
+++ b/stm/mma.c
@@ -6,6 +6,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "systick.h"
 #include "obj.h"
 #include "runtime.h"
diff --git a/stm/printf.c b/stm/printf.c
index 732e8345268634c831a19d0a3f1674d7bd68cf28..82b168d1c4a36c6154b0ca00637a21d271d52750 100644
--- a/stm/printf.c
+++ b/stm/printf.c
@@ -1,9 +1,12 @@
 #include <stdint.h>
+#include <string.h>
 #include <stdarg.h>
+
 #include "std.h"
 #include "misc.h"
 #include "systick.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "lcd.h"
 #include "usart.h"
diff --git a/stm/pybwlan.c b/stm/pybwlan.c
index 0c9b5501eef9d9a70f9cb40cf2a99a09d23f61f2..e403a645eec0f77f357b8426f9ab0be37a6fb881 100644
--- a/stm/pybwlan.c
+++ b/stm/pybwlan.c
@@ -1,5 +1,6 @@
 #include <stdlib.h>
 #include <stdint.h>
+#include <string.h>
 #include <stm32f4xx.h>
 #include <stm32f4xx_rcc.h>
 #include <stm32f4xx_gpio.h>
@@ -12,6 +13,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "systick.h"
 
 #include "nlr.h"
@@ -58,7 +60,7 @@ mp_obj_t decode_addr(unsigned char *ip, int n_bytes) {
     } else if (n_bytes == 32) {
         snprintf(data, 64, "%s", ip);
     }
-    return mp_obj_new_str(qstr_from_strn_copy(data, strlen(data)));
+    return mp_obj_new_str(qstr_from_strn(data, strlen(data)));
 }
 
 void decode_addr_and_store(mp_obj_t object, qstr q_attr, unsigned char *ip, int n_bytes) {
@@ -78,20 +80,20 @@ mp_obj_t pyb_wlan_get_ip(void) {
 
     // if it doesn't already exist, make a new empty class for NetAddress objects
     if (net_address_type == MP_OBJ_NULL) {
-        net_address_type = mp_obj_new_type(qstr_from_str_static("NetAddress"), mp_const_empty_tuple, mp_obj_new_dict(0));
+        net_address_type = mp_obj_new_type(QSTR_FROM_STR_STATIC("NetAddress"), mp_const_empty_tuple, mp_obj_new_dict(0));
     }
 
     // make a new NetAddress object
     mp_obj_t net_addr = rt_call_function_0(net_address_type);
 
     // fill the NetAddress object with data
-    decode_addr_and_store(net_addr, qstr_from_str_static("ip"), &ipconfig.aucIP[0], 4);
-    decode_addr_and_store(net_addr, qstr_from_str_static("subnet"), &ipconfig.aucSubnetMask[0], 4);
-    decode_addr_and_store(net_addr, qstr_from_str_static("gateway"), &ipconfig.aucDefaultGateway[0], 4);
-    decode_addr_and_store(net_addr, qstr_from_str_static("dhcp"), &ipconfig.aucDHCPServer[0], 4);
-    decode_addr_and_store(net_addr, qstr_from_str_static("dns"), &ipconfig.aucDNSServer[0], 4);
-    decode_addr_and_store(net_addr, qstr_from_str_static("mac"), &ipconfig.uaMacAddr[0], 6);
-    decode_addr_and_store(net_addr, qstr_from_str_static("ssid"), &ipconfig.uaSSID[0], 32);
+    decode_addr_and_store(net_addr, QSTR_FROM_STR_STATIC("ip"), &ipconfig.aucIP[0], 4);
+    decode_addr_and_store(net_addr, QSTR_FROM_STR_STATIC("subnet"), &ipconfig.aucSubnetMask[0], 4);
+    decode_addr_and_store(net_addr, QSTR_FROM_STR_STATIC("gateway"), &ipconfig.aucDefaultGateway[0], 4);
+    decode_addr_and_store(net_addr, QSTR_FROM_STR_STATIC("dhcp"), &ipconfig.aucDHCPServer[0], 4);
+    decode_addr_and_store(net_addr, QSTR_FROM_STR_STATIC("dns"), &ipconfig.aucDNSServer[0], 4);
+    decode_addr_and_store(net_addr, QSTR_FROM_STR_STATIC("mac"), &ipconfig.uaMacAddr[0], 6);
+    decode_addr_and_store(net_addr, QSTR_FROM_STR_STATIC("ssid"), &ipconfig.uaSSID[0], 32);
 
     return net_addr;
 }
@@ -122,12 +124,12 @@ mp_obj_t pyb_wlan_http_get(mp_obj_t host_name, mp_obj_t host_path) {
         last_ip = (192 << 24) | (168 << 16) | (0 << 8) | (3);
     } else {
         if (pyb_wlan_get_host(host_name) == mp_const_none) {
-            nlr_jump(mp_obj_new_exception_msg(qstr_from_str_static("WlanError"), "unknown host"));
+            nlr_jump(mp_obj_new_exception_msg(QSTR_FROM_STR_STATIC("WlanError"), "unknown host"));
         }
     }
     int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (sd < 0) {
-        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "socket failed: %d", (void*)sd));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(QSTR_FROM_STR_STATIC("WlanError"), "socket failed: %d", (void*)sd));
     }
     //printf("socket seemed to work\n");
     //sys_tick_delay_ms(200);
@@ -138,7 +140,7 @@ mp_obj_t pyb_wlan_http_get(mp_obj_t host_name, mp_obj_t host_path) {
     remote.sin_addr.s_addr = htonl(last_ip);
     int ret = connect(sd, (sockaddr*)&remote, sizeof(sockaddr));
     if (ret != 0) {
-        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "connect failed: %d", (void*)ret));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(QSTR_FROM_STR_STATIC("WlanError"), "connect failed: %d", (void*)ret));
     }
     //printf("connect seemed to work\n");
     //sys_tick_delay_ms(200);
@@ -159,7 +161,7 @@ mp_obj_t pyb_wlan_http_get(mp_obj_t host_name, mp_obj_t host_path) {
             ret = send(sd, query + sent, strlen(query + sent), 0);
             //printf("sent %d bytes\n", ret);
             if (ret < 0) {
-                nlr_jump(mp_obj_new_exception_msg(qstr_from_str_static("WlanError"), "send failed"));
+                nlr_jump(mp_obj_new_exception_msg(QSTR_FROM_STR_STATIC("WlanError"), "send failed"));
             }
             sent += ret;
             //sys_tick_delay_ms(200);
@@ -196,12 +198,12 @@ mp_obj_t pyb_wlan_http_get(mp_obj_t host_name, mp_obj_t host_path) {
             // read data
             ret = recv(sd, buf, 64, 0);
             if (ret < 0) {
-                nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "recv failed %d", (void*)ret));
+                nlr_jump(mp_obj_new_exception_msg_1_arg(QSTR_FROM_STR_STATIC("WlanError"), "recv failed %d", (void*)ret));
             }
             vstr_add_strn(vstr, buf, ret);
         }
 
-        mp_ret = mp_obj_new_str(qstr_from_str_take(vstr->buf, vstr->alloc));
+        mp_ret = mp_obj_new_str(qstr_from_strn_take(vstr->buf, vstr->alloc, vstr->len));
     }
 
     closesocket(sd);
@@ -216,7 +218,7 @@ mp_obj_t pyb_wlan_serve(void) {
     sys_tick_delay_ms(500);
     if (sd < 0) {
         printf("socket fail\n");
-        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "socket failed: %d", (void*)sd));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(QSTR_FROM_STR_STATIC("WlanError"), "socket failed: %d", (void*)sd));
     }
 
     /*
@@ -237,7 +239,7 @@ mp_obj_t pyb_wlan_serve(void) {
     sys_tick_delay_ms(100);
     if (ret != 0) {
         printf("bind fail\n");
-        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "bind failed: %d", (void*)ret));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(QSTR_FROM_STR_STATIC("WlanError"), "bind failed: %d", (void*)ret));
     }
     printf("bind seemed to work\n");
 
@@ -355,14 +357,14 @@ void pyb_wlan_init(void) {
     SpiInit();
     wlan_init(CC3000_UsynchCallback, sendWLFWPatch, sendDriverPatch, sendBootLoaderPatch, ReadWlanInterruptPin, WlanInterruptEnable, WlanInterruptDisable, WriteWlanPin);
 
-    mp_obj_t m = mp_obj_new_module(qstr_from_str_static("wlan"));
-    rt_store_attr(m, qstr_from_str_static("connect"), rt_make_function_var(0, pyb_wlan_connect));
-    rt_store_attr(m, qstr_from_str_static("disconnect"), rt_make_function_n(0, pyb_wlan_disconnect));
-    rt_store_attr(m, qstr_from_str_static("ip"), rt_make_function_n(0, pyb_wlan_get_ip));
-    rt_store_attr(m, qstr_from_str_static("get_host"), rt_make_function_n(1, pyb_wlan_get_host));
-    rt_store_attr(m, qstr_from_str_static("http_get"), rt_make_function_n(2, pyb_wlan_http_get));
-    rt_store_attr(m, qstr_from_str_static("serve"), rt_make_function_n(0, pyb_wlan_serve));
-    rt_store_name(qstr_from_str_static("wlan"), m);
+    mp_obj_t m = mp_obj_new_module(QSTR_FROM_STR_STATIC("wlan"));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("connect"), rt_make_function_var(0, pyb_wlan_connect));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("disconnect"), rt_make_function_n(0, pyb_wlan_disconnect));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("ip"), rt_make_function_n(0, pyb_wlan_get_ip));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("get_host"), rt_make_function_n(1, pyb_wlan_get_host));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("http_get"), rt_make_function_n(2, pyb_wlan_http_get));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("serve"), rt_make_function_n(0, pyb_wlan_serve));
+    rt_store_name(QSTR_FROM_STR_STATIC("wlan"), m);
 }
 
 void pyb_wlan_start(void) {
diff --git a/stm/qstrdefsport.h b/stm/qstrdefsport.h
new file mode 100644
index 0000000000000000000000000000000000000000..7e6f93e423c86a5838c86569ae541453f2dbbf92
--- /dev/null
+++ b/stm/qstrdefsport.h
@@ -0,0 +1,30 @@
+// qstrs specific to this port
+
+Q(help)
+Q(pyb)
+Q(info)
+Q(sd_test)
+Q(stop)
+Q(standby)
+Q(source_dir)
+Q(main)
+Q(sync)
+Q(gc)
+Q(delay)
+Q(led)
+Q(switch)
+Q(servo)
+Q(pwm)
+Q(accel)
+Q(mma_read)
+Q(mma_mode)
+Q(hid)
+Q(time)
+Q(rand)
+Q(Led)
+Q(Servo)
+Q(I2C)
+Q(gpio)
+Q(Usart)
+Q(ADC)
+Q(open)
diff --git a/stm/servo.c b/stm/servo.c
index f89c97f1c578eb5327366ef36d90cf603ca5e147..31d65283b51bd349a6ff3472e808ad6e55d09cc5 100644
--- a/stm/servo.c
+++ b/stm/servo.c
@@ -6,6 +6,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "servo.h"
 
diff --git a/stm/std.h b/stm/std.h
index 95c606e0584d38fd77a208e87b2f1cc7117ca075..e7002b06f6f55f33a323123e63547a7546b76042 100644
--- a/stm/std.h
+++ b/stm/std.h
@@ -11,7 +11,7 @@ void *memcpy(void *dest, const void *src, size_t n);
 void *memmove(void *dest, const void *src, size_t n);
 void *memset(void *s, int c, size_t n);
 
-int strlen(const char *str);
+//int strlen(const char *str);
 int strcmp(const char *s1, const char *s2);
 int strncmp(const char *s1, const char *s2, size_t n);
 char *strndup(const char *s, size_t n);
diff --git a/stm/stm32fxxx_it.c b/stm/stm32fxxx_it.c
index c1a9a0702ff485fec154d55177bd971ce6a28fd1..0cece8854ffff48c40277e9e1c8bd3c7ca228bee 100644
--- a/stm/stm32fxxx_it.c
+++ b/stm/stm32fxxx_it.c
@@ -271,6 +271,7 @@ void TIM6_DAC_IRQHandler(void) {
 #include "std.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "led.h"
 // EXTI
diff --git a/stm/storage.c b/stm/storage.c
index daee4adb5ee66437d24bc6fbc7015e49487d6949..a684fcb0a0d0619ac8991a662fdb7797e7d490df 100644
--- a/stm/storage.c
+++ b/stm/storage.c
@@ -4,6 +4,7 @@
 #include "misc.h"
 #include "systick.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "led.h"
 #include "flash.h"
diff --git a/stm/timer.c b/stm/timer.c
index 062f8c689e263d46734bf503435e67efbcc73577..d20d3a77bd96bf7ec2a6d92ae780fb460b93b8ca 100644
--- a/stm/timer.c
+++ b/stm/timer.c
@@ -1,5 +1,6 @@
 #include <stdint.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "stm_misc.h"
 #include "stm32f4xx_rcc.h"
@@ -8,6 +9,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "parse.h"
 #include "obj.h"
 #include "runtime.h"
@@ -71,12 +73,12 @@ void timer_init(void) {
     TIM_Cmd(TIM6, ENABLE);
 
     // Python interface
-    mp_obj_t m = mp_obj_new_module(qstr_from_str_static("timer"));
-    rt_store_attr(m, qstr_from_str_static("callback"), rt_make_function_n(1, timer_py_set_callback));
-    rt_store_attr(m, qstr_from_str_static("period"), rt_make_function_n(1, timer_py_set_period));
-    rt_store_attr(m, qstr_from_str_static("prescaler"), rt_make_function_n(1, timer_py_set_prescaler));
-    rt_store_attr(m, qstr_from_str_static("value"), rt_make_function_n(0, timer_py_get_value));
-    rt_store_name(qstr_from_str_static("timer"), m);
+    mp_obj_t m = mp_obj_new_module(QSTR_FROM_STR_STATIC("timer"));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("callback"), rt_make_function_n(1, timer_py_set_callback));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("period"), rt_make_function_n(1, timer_py_set_period));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("prescaler"), rt_make_function_n(1, timer_py_set_prescaler));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("value"), rt_make_function_n(0, timer_py_get_value));
+    rt_store_name(QSTR_FROM_STR_STATIC("timer"), m);
 }
 
 void timer_interrupt(void) {
diff --git a/stm/usart.c b/stm/usart.c
index 796516f54763fe64acf4e06d21218f59a40ceb85..c687cff05f20f0b2a6aab6ea543de9289ac42f32 100644
--- a/stm/usart.c
+++ b/stm/usart.c
@@ -5,6 +5,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "usart.h"
 
diff --git a/stm/usrsw.c b/stm/usrsw.c
index 9d1ee50ffb47139091306648199e16071bf3a555..dc2b03f3db8007b0b79d9f61f7b363fe5bdc7d84 100644
--- a/stm/usrsw.c
+++ b/stm/usrsw.c
@@ -6,6 +6,7 @@
 
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "obj.h"
 #include "usrsw.h"
 
diff --git a/unix-cpy/Makefile b/unix-cpy/Makefile
index 4955ea0c50626a7a3bc0d421a5512c5b2220ce87..f8ab73c354234ee1bb83268bf21bd4e6f1e1e71c 100644
--- a/unix-cpy/Makefile
+++ b/unix-cpy/Makefile
@@ -25,7 +25,7 @@ endif
 SRC_C = \
 	main.c \
 
-OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) $(PY_O)
+OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
 LIB =
 
 $(PROG): $(BUILD) $(OBJ)
diff --git a/unix/.gitignore b/unix/.gitignore
index a90e4976e694dbf88ceb3c4bf4ace4928f6e42d0..390bdd300e300eb0d8f18782127ff9714741b984 100644
--- a/unix/.gitignore
+++ b/unix/.gitignore
@@ -1,3 +1,3 @@
 build
-py
+micropython
 *.py
diff --git a/unix/Makefile b/unix/Makefile
index 454ded79af4d837e49eb7846b448fd2fa520ba9c..7a4ce3e161620001f2995a1f16547e3d398da996 100644
--- a/unix/Makefile
+++ b/unix/Makefile
@@ -2,6 +2,9 @@
 PROG = micropython
 all: $(PROG)
 
+# qstr definitions (must come before including py.mk)
+QSTR_DEFS = qstrdefsport.h
+
 # include py core make definitions
 include ../py/py.mk
 
@@ -27,7 +30,7 @@ SRC_C = \
 	file.c \
 	socket.c \
 
-OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) $(PY_O)
+OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
 LIB = -lreadline
 # the following is needed for BSD
 #LIB += -ltermcap
@@ -40,7 +43,7 @@ ifndef DEBUG
 endif
 	$(Q)size $(PROG)
 
-$(BUILD)/%.o: %.c
+$(BUILD)/%.o: %.c $(QSTR_DEFS)
 	$(ECHO) "CC $<"
 	$(Q)$(CC) $(CFLAGS) -c -o $@ $<
 
diff --git a/unix/file.c b/unix/file.c
index af759447118055c78e6a5861dfcccf62b227bb61..72c56ca100c59c45ac5f16c56326f7a8401b792c 100644
--- a/unix/file.c
+++ b/unix/file.c
@@ -6,7 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "stream.h"
 
diff --git a/unix/main.c b/unix/main.c
index 08567c04dcf4b26ef0f4802b75abf675742f87a4..74e903c2efba2bc2b8297dc65c6e66015ae3d386 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -6,6 +6,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
+#include "qstr.h"
 #include "lexer.h"
 #include "lexerunix.h"
 #include "parse.h"
@@ -216,12 +217,12 @@ int main(int argc, char **argv) {
     qstr_init();
     rt_init();
 
-    mp_obj_t m_sys = mp_obj_new_module(qstr_from_str_static("sys"));
+    mp_obj_t m_sys = mp_obj_new_module(MP_QSTR_sys);
     mp_obj_t py_argv = mp_obj_new_list(0, NULL);
-    rt_store_attr(m_sys, qstr_from_str_static("argv"), py_argv);
+    rt_store_attr(m_sys, MP_QSTR_argv, py_argv);
 
-    rt_store_name(qstr_from_str_static("test"), test_obj_new(42));
-    rt_store_name(qstr_from_str_static("open"), (mp_obj_t)&mp_builtin_open_obj);
+    rt_store_name(qstr_from_str("test"), test_obj_new(42));
+    rt_store_name(MP_QSTR_open, (mp_obj_t)&mp_builtin_open_obj);
     rawsocket_init();
 
     // Here is some example code to create a class and instance of that class.
@@ -232,9 +233,9 @@ int main(int argc, char **argv) {
     // test_obj = TestClass()
     // test_obj.attr = 42
     mp_obj_t test_class_type, test_class_instance;
-    test_class_type = mp_obj_new_type(qstr_from_str_static("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0));
-    rt_store_name(qstr_from_str_static("test_obj"), test_class_instance = rt_call_function_0(test_class_type));
-    rt_store_attr(test_class_instance, qstr_from_str_static("attr"), mp_obj_new_int(42));
+    test_class_type = mp_obj_new_type(QSTR_FROM_STR_STATIC("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0));
+    rt_store_name(QSTR_FROM_STR_STATIC("test_obj"), test_class_instance = rt_call_function_0(test_class_type));
+    rt_store_attr(test_class_instance, QSTR_FROM_STR_STATIC("attr"), mp_obj_new_int(42));
 
     /*
     printf("bytes:\n");
@@ -259,7 +260,7 @@ int main(int argc, char **argv) {
                 }
             } else {
                 for (int i = a; i < argc; i++) {
-                    rt_list_append(py_argv, MP_OBJ_NEW_QSTR(qstr_from_strn_copy(argv[i], strlen(argv[i]))));
+                    rt_list_append(py_argv, MP_OBJ_NEW_QSTR(qstr_from_str(argv[i])));
                 }
                 do_file(argv[a]);
                 break;
diff --git a/unix/qstrdefsport.h b/unix/qstrdefsport.h
new file mode 100644
index 0000000000000000000000000000000000000000..3178f0a826efa9dab888ea54d8adb6eda059cfea
--- /dev/null
+++ b/unix/qstrdefsport.h
@@ -0,0 +1,13 @@
+// qstrs specific to this port
+
+Q(sys)
+Q(argv)
+Q(open)
+Q(rawsocket)
+Q(socket)
+Q(sockaddr_in)
+Q(htons)
+Q(inet_aton)
+Q(gethostbyname)
+Q(getaddrinfo)
+Q(rawsocket)
diff --git a/unix/socket.c b/unix/socket.c
index 708cf7af525702d8027487ced81b5893849d24c7..e43695c6b6a27463f0100d9d44279bcb7da4468d 100644
--- a/unix/socket.c
+++ b/unix/socket.c
@@ -10,7 +10,7 @@
 #include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
-#include "mpqstr.h"
+#include "qstr.h"
 #include "obj.h"
 #include "objtuple.h"
 #include "objarray.h"
@@ -249,7 +249,7 @@ static mp_obj_t mod_socket_getaddrinfo(uint n_args, const mp_obj_t *args) {
         // "canonname will be a string representing the canonical name of the host
         // if AI_CANONNAME is part of the flags argument; else canonname will be empty." ??
         if (addr->ai_canonname) {
-            t->items[3] = MP_OBJ_NEW_QSTR(qstr_from_strn_copy(addr->ai_canonname, strlen(addr->ai_canonname)));
+            t->items[3] = MP_OBJ_NEW_QSTR(qstr_from_str(addr->ai_canonname));
         } else {
             t->items[3] = mp_const_none;
         }
@@ -262,23 +262,23 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_socket_getaddrinfo_obj, 2, 6, mod
 
 extern mp_obj_type_t sockaddr_in_type;
 
-#define STORE_INT_CONST(m, name) rt_store_attr(m, qstr_from_str_static(#name), MP_OBJ_NEW_SMALL_INT(name))
+#define STORE_INT_CONST(m, name) rt_store_attr(m, QSTR_FROM_STR_STATIC(#name), MP_OBJ_NEW_SMALL_INT(name))
 
 void rawsocket_init() {
-    mp_obj_t m = mp_obj_new_module(qstr_from_str_static("rawsocket"));
-    rt_store_attr(m, qstr_from_str_static("socket"), (mp_obj_t)&rawsocket_type);
+    mp_obj_t m = mp_obj_new_module(MP_QSTR_rawsocket);
+    rt_store_attr(m, MP_QSTR_socket, (mp_obj_t)&rawsocket_type);
 #if MICROPY_SOCKET_EXTRA
-    rt_store_attr(m, qstr_from_str_static("sockaddr_in"), (mp_obj_t)&sockaddr_in_type);
-    rt_store_attr(m, qstr_from_str_static("htons"), (mp_obj_t)&mod_socket_htons_obj);
-    rt_store_attr(m, qstr_from_str_static("inet_aton"), (mp_obj_t)&mod_socket_inet_aton_obj);
-    rt_store_attr(m, qstr_from_str_static("gethostbyname"), (mp_obj_t)&mod_socket_gethostbyname_obj);
+    rt_store_attr(m, MP_QSTR_sockaddr_in, (mp_obj_t)&sockaddr_in_type);
+    rt_store_attr(m, MP_QSTR_htons, (mp_obj_t)&mod_socket_htons_obj);
+    rt_store_attr(m, MP_QSTR_inet_aton, (mp_obj_t)&mod_socket_inet_aton_obj);
+    rt_store_attr(m, MP_QSTR_gethostbyname, (mp_obj_t)&mod_socket_gethostbyname_obj);
 #endif
-    rt_store_attr(m, qstr_from_str_static("getaddrinfo"), (mp_obj_t)&mod_socket_getaddrinfo_obj);
+    rt_store_attr(m, MP_QSTR_getaddrinfo, (mp_obj_t)&mod_socket_getaddrinfo_obj);
     STORE_INT_CONST(m, AF_UNIX);
     STORE_INT_CONST(m, AF_INET);
     STORE_INT_CONST(m, AF_INET6);
     STORE_INT_CONST(m, SOCK_STREAM);
     STORE_INT_CONST(m, SOCK_DGRAM);
     STORE_INT_CONST(m, SOCK_RAW);
-    rt_store_name(qstr_from_str_static("rawsocket"), m);
+    rt_store_name(MP_QSTR_rawsocket, m);
 }