# HG changeset patch
# User Bob Ippolito <bob@redivi.com>
# Date 1288924853 0
#      Fri Nov 05 02:40:53 2010 +0000
# Node ID eb69b441a80b4cd2a2771215fc68dde7a54db37e
# Parent  3b0453521afb569dd59fb94d93d304690876b25c
http://code.google.com/p/simplejson/issues/detail?id=86

git-svn-id: http://simplejson.googlecode.com/svn/trunk@240 a4795897-2c25-0410-b006-0d3caba88fa1

diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,8 @@
+Version 2.1.3 released XXXX-XX-XX
+
+* Support the sort_keys option in C encoding speedups
+  http://code.google.com/p/simplejson/issues/detail?id=86
+
 Version 2.1.2 released 2010-11-01
 
 * Correct wrong end when object_pairs_hook is used
diff --git a/simplejson/_speedups.c b/simplejson/_speedups.c
--- a/simplejson/_speedups.c
+++ b/simplejson/_speedups.c
@@ -2185,9 +2185,9 @@
     static PyObject *iteritems = NULL;
     PyObject *kstr = NULL;
     PyObject *ident = NULL;
-    PyObject *key, *value;
     PyObject *iter = NULL;
     PyObject *item = NULL;
+    PyObject *items = NULL;
     PyObject *encoded = NULL;
     int skipkeys;
     Py_ssize_t idx;
@@ -2232,22 +2232,61 @@
         */
     }
 
-    /* TODO: C speedup not implemented for sort_keys */
+    if (PyObject_IsTrue(s->sort_keys)) {
+        /* First sort the keys then replace them with (key, value) tuples. */
+        Py_ssize_t i, nitems;
+        if (PyDict_CheckExact(dct))
+            items = PyDict_Keys(dct);
+        else
+            items = PyMapping_Keys(dct);
+        if (items == NULL)
+            goto bail;
+        if (!PyList_Check(items)) {
+            PyErr_SetString(PyExc_ValueError, "keys must return list");
+            goto bail;
+        }
+        if (PyList_Sort(items) < 0)
+            goto bail;
+        nitems = PyList_GET_SIZE(items);
+        for (i = 0; i < nitems; i++) {
+            PyObject *key, *value;
+            key = PyList_GET_ITEM(items, i);
+            value = PyDict_GetItem(dct, key);
+            item = PyTuple_Pack(2, key, value);
+            if (item == NULL)
+                goto bail;
+            PyList_SET_ITEM(items, i, item);
+            Py_DECREF(key);
+        }
+    }
+    else {
+        if (PyDict_CheckExact(dct))
+            items = PyDict_Items(dct);
+        else
+            items = PyMapping_Items(dct);
+    }
+    if (items == NULL)
+        goto bail;
+    iter = PyObject_GetIter(items);
+    Py_DECREF(items);
+    if (iter == NULL)
+        goto bail;
 
     skipkeys = PyObject_IsTrue(s->skipkeys);
     idx = 0;
-    iter = PyObject_CallMethodObjArgs(dct, iteritems, NULL);
-    if (iter == NULL)
-        goto bail;
     while ((item = PyIter_Next(iter))) {
-
-        key = PyTuple_GetItem(item, 0);
+        PyObject *encoded, *key, *value;
+        if (!PyTuple_Check(item) || Py_SIZE(item) != 2) {
+            PyErr_SetString(PyExc_ValueError, "items must return 2-tuples");
+            goto bail;
+        }
+        key = PyTuple_GET_ITEM(item, 0);
         if (key == NULL)
             goto bail;
-        value = PyTuple_GetItem(item, 1);
+        value = PyTuple_GET_ITEM(item, 1);
         if (value == NULL)
             goto bail;
-        
+
         encoded = PyDict_GetItem(s->key_memo, key);
         if (encoded != NULL) {
             Py_INCREF(encoded);
@@ -2261,13 +2300,15 @@
             if (kstr == NULL)
                 goto bail;
         }
-        else if (PyInt_Check(key) || PyLong_Check(key)) {
-            kstr = PyObject_Str(key);
+        else if (key == Py_True || key == Py_False || key == Py_None) {
+            /* This must come before the PyInt_Check because
+               True and False are also 1 and 0.*/
+            kstr = _encoded_const(key);
             if (kstr == NULL)
                 goto bail;
         }
-        else if (key == Py_True || key == Py_False || key == Py_None) {
-            kstr = _encoded_const(key);
+        else if (PyInt_Check(key) || PyLong_Check(key)) {
+            kstr = PyObject_Str(key);
             if (kstr == NULL)
                 goto bail;
         }
@@ -2326,7 +2367,7 @@
 
 bail:
     Py_XDECREF(encoded);
-    Py_XDECREF(item);
+    Py_XDECREF(items);
     Py_XDECREF(iter);
     Py_XDECREF(kstr);
     Py_XDECREF(ident);
diff --git a/simplejson/encoder.py b/simplejson/encoder.py
--- a/simplejson/encoder.py
+++ b/simplejson/encoder.py
@@ -268,7 +268,7 @@
 
         key_memo = {}
         if (_one_shot and c_make_encoder is not None
-                and self.indent is None and not self.sort_keys):
+                and self.indent is None):
             _iterencode = c_make_encoder(
                 markers, self.default, _encoder, self.indent,
                 self.key_separator, self.item_separator, self.sort_keys,
diff --git a/simplejson/tests/test_encode_basestring_ascii.py b/simplejson/tests/test_encode_basestring_ascii.py
--- a/simplejson/tests/test_encode_basestring_ascii.py
+++ b/simplejson/tests/test_encode_basestring_ascii.py
@@ -39,3 +39,8 @@
             #        result, expect, fname, input_string))
             self.assertEquals(result, expect,
                 '%r != %r for %s(%r)' % (result, expect, fname, input_string))
+
+    def test_sorted_dict(self):
+        items = [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]
+        s = simplejson.dumps(dict(items), sort_keys=True)
+        self.assertEqual(s, '{"five": 5, "four": 4, "one": 1, "three": 3, "two": 2}')