New page-merge-mm, page-merge-stk tests.
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 4 Jan 2007 14:19:25 +0000 (14:19 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 4 Jan 2007 14:19:25 +0000 (14:19 +0000)
Thanks to Godmar Back for suggestion.

14 files changed:
src/tests/vm/Make.tests
src/tests/vm/Rubric.functionality
src/tests/vm/child-qsort-mm.c [new file with mode: 0644]
src/tests/vm/child-qsort.c [new file with mode: 0644]
src/tests/vm/child-sort.c
src/tests/vm/page-merge-mm.c [new file with mode: 0644]
src/tests/vm/page-merge-mm.ck [new file with mode: 0644]
src/tests/vm/page-merge-par.c
src/tests/vm/page-merge-stk.c [new file with mode: 0644]
src/tests/vm/page-merge-stk.ck [new file with mode: 0644]
src/tests/vm/parallel-merge.c [new file with mode: 0644]
src/tests/vm/parallel-merge.h [new file with mode: 0644]
src/tests/vm/qsort.c [new file with mode: 0644]
src/tests/vm/qsort.h [new file with mode: 0644]

index a2427a165f799cc5b5fbfbfd31de3fced4864d6f..04b1b81666ac0407ffd05a7157048eabc6e6f911 100644 (file)
@@ -3,13 +3,14 @@
 tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha     \
 pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code       \
 pt-write-code2 pt-grow-stk-sc page-linear page-parallel page-merge-seq \
-page-merge-par page-shuffle mmap-read mmap-close mmap-unmap            \
-mmap-overlap mmap-twice mmap-write mmap-exit mmap-shuffle mmap-bad-fd  \
-mmap-clean mmap-inherit mmap-misalign mmap-null mmap-over-code         \
-mmap-over-data mmap-over-stk mmap-remove mmap-zero)
+page-merge-par page-merge-stk page-merge-mm page-shuffle mmap-read     \
+mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit     \
+mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign         \
+mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove      \
+mmap-zero)
 
 tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear  \
-child-sort child-mm-wrt child-inherit)
+child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit)
 
 tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c     \
 tests/cksum.c tests/lib.c tests/main.c
@@ -28,8 +29,12 @@ tests/lib.c tests/main.c
 tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c
 tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c   \
 tests/lib.c tests/main.c
-tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c tests/arc4.c   \
-tests/lib.c tests/main.c
+tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
+tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
+tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
 tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c       \
 tests/cksum.c tests/lib.c tests/main.c
 tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c
@@ -56,6 +61,9 @@ tests/vm/mmap-remove_SRC = tests/vm/mmap-remove.c tests/lib.c tests/main.c
 tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c
 
 tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c
+tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c
+tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \
+tests/lib.c
 tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c
 tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c
 tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c
@@ -71,6 +79,8 @@ tests/vm/mmap-exit_PUTFILES = tests/vm/child-mm-wrt
 tests/vm/page-parallel_PUTFILES = tests/vm/child-linear
 tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort
 tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort
+tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort
+tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm
 tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt
 tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit
 tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt
index a27ad1a533ed373887df10b726a215148c2a09d2..8338e7fd22fe682852ebc470a02f9acd10022d59 100644 (file)
@@ -11,6 +11,8 @@ Functionality of virtual memory subsystem:
 3      page-shuffle
 5      page-merge-seq
 5      page-merge-par
+5      page-merge-mm
+5      page-merge-stk
 
 - Test "mmap" system call.
 2      mmap-read
diff --git a/src/tests/vm/child-qsort-mm.c b/src/tests/vm/child-qsort-mm.c
new file mode 100644 (file)
index 0000000..db45499
--- /dev/null
@@ -0,0 +1,25 @@
+/* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort,
+   a multi-pass divide and conquer algorithm.  */
+
+#include <debug.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/vm/qsort.h"
+
+const char *test_name = "child-qsort-mm";
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  int handle;
+  unsigned char *p = (unsigned char *) 0x10000000;
+
+  quiet = true;
+
+  CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
+  CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]);
+  qsort_bytes (p, 1024 * 128);
+  
+  return 80;
+}
diff --git a/src/tests/vm/child-qsort.c b/src/tests/vm/child-qsort.c
new file mode 100644 (file)
index 0000000..355f4eb
--- /dev/null
@@ -0,0 +1,32 @@
+/* Reads a 128 kB file onto the stack and "sorts" the bytes in
+   it, using quick sort, a multi-pass divide and conquer
+   algorithm.  The sorted data is written back to the same file
+   in-place. */
+
+#include <debug.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/vm/qsort.h"
+
+const char *test_name = "child-qsort";
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  int handle;
+  unsigned char buf[128 * 1024];
+  size_t size;
+
+  quiet = true;
+
+  CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
+
+  size = read (handle, buf, sizeof buf);
+  qsort_bytes (buf, sizeof buf);
+  seek (handle, 0);
+  write (handle, buf, size);
+  close (handle);
+  
+  return 72;
+}
index d755432cbd6dde76a2a34754bce422c3dafcb3d9..dff2c772050167e4227f314e6fdea2e34b5c4437 100644 (file)
@@ -1,6 +1,6 @@
-/* Reads a 128 kB file and "sorts" the bytes in it, using
-   counting sort.  The sorted data is written back to the same
-   file in-place. */
+/* Reads a 128 kB file into static data and "sorts" the bytes in
+   it, using counting sort, a single-pass algorithm.  The sorted
+   data is written back to the same file in-place. */
 
 #include <debug.h>
 #include <syscall.h>
diff --git a/src/tests/vm/page-merge-mm.c b/src/tests/vm/page-merge-mm.c
new file mode 100644 (file)
index 0000000..908c71c
--- /dev/null
@@ -0,0 +1,8 @@
+#include "tests/main.h"
+#include "tests/vm/parallel-merge.h"
+
+void
+test_main (void) 
+{
+  parallel_merge ("child-qsort-mm", 80);
+}
diff --git a/src/tests/vm/page-merge-mm.ck b/src/tests/vm/page-merge-mm.ck
new file mode 100644 (file)
index 0000000..74fa980
--- /dev/null
@@ -0,0 +1,29 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-mm) begin
+(page-merge-mm) init
+(page-merge-mm) sort chunk 0
+(page-merge-mm) sort chunk 1
+(page-merge-mm) sort chunk 2
+(page-merge-mm) sort chunk 3
+(page-merge-mm) sort chunk 4
+(page-merge-mm) sort chunk 5
+(page-merge-mm) sort chunk 6
+(page-merge-mm) sort chunk 7
+(page-merge-mm) wait for child 0
+(page-merge-mm) wait for child 1
+(page-merge-mm) wait for child 2
+(page-merge-mm) wait for child 3
+(page-merge-mm) wait for child 4
+(page-merge-mm) wait for child 5
+(page-merge-mm) wait for child 6
+(page-merge-mm) wait for child 7
+(page-merge-mm) merge
+(page-merge-mm) verify
+(page-merge-mm) success, buf_idx=1,048,576
+(page-merge-mm) end
+EOF
+pass;
index d588bb0a0794dccc751498194e32b67493c61127..e7e16093ce12c3ca1cd3aac33ebf46afba34f4a6 100644 (file)
@@ -1,147 +1,8 @@
-/* Generates about 1 MB of random data that is then divided into
-   16 chunks.  A separate subprocess sorts each chunk; the
-   subprocesses run in parallel.  Then we merge the chunks and
-   verify that the result is what it should be. */
-
-#include <stdio.h>
-#include <syscall.h>
-#include "tests/arc4.h"
-#include "tests/lib.h"
 #include "tests/main.h"
-
-#define CHUNK_SIZE (128 * 1024)
-#define CHUNK_CNT 8                             /* Number of chunks. */
-#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE)      /* Buffer size. */
-
-unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
-size_t histogram[256];
-
-/* Initialize buf1 with random data,
-   then count the number of instances of each value within it. */
-static void
-init (void) 
-{
-  struct arc4 arc4;
-  size_t i;
-
-  msg ("init");
-
-  arc4_init (&arc4, "foobar", 6);
-  arc4_crypt (&arc4, buf1, sizeof buf1);
-  for (i = 0; i < sizeof buf1; i++)
-    histogram[buf1[i]]++;
-}
-
-/* Sort each chunk of buf1 using a subprocess. */
-static void
-sort_chunks (void)
-{
-  pid_t children[CHUNK_CNT];
-  size_t i;
-
-  for (i = 0; i < CHUNK_CNT; i++) 
-    {
-      char fn[128];
-      char cmd[128];
-      int handle;
-
-      msg ("sort chunk %zu", i);
-
-      /* Write this chunk to a file. */
-      snprintf (fn, sizeof fn, "buf%zu", i);
-      create (fn, CHUNK_SIZE);
-      quiet = true;
-      CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
-      write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
-      close (handle);
-
-      /* Sort with subprocess. */
-      snprintf (cmd, sizeof cmd, "child-sort %s", fn);
-      CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd);
-      quiet = false;
-    }
-
-  for (i = 0; i < CHUNK_CNT; i++) 
-    {
-      char fn[128];
-      int handle;
-
-      CHECK (wait (children[i]) == 123, "wait for child %zu", i);
-
-      /* Read chunk back from file. */
-      quiet = true;
-      snprintf (fn, sizeof fn, "buf%zu", i);
-      CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
-      read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
-      close (handle);
-      quiet = false;
-    }
-}
-
-/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
-static void
-merge (void) 
-{
-  unsigned char *mp[CHUNK_CNT];
-  size_t mp_left;
-  unsigned char *op;
-  size_t i;
-
-  msg ("merge");
-
-  /* Initialize merge pointers. */
-  mp_left = CHUNK_CNT;
-  for (i = 0; i < CHUNK_CNT; i++)
-    mp[i] = buf1 + CHUNK_SIZE * i;
-
-  /* Merge. */
-  op = buf2;
-  while (mp_left > 0) 
-    {
-      /* Find smallest value. */
-      size_t min = 0;
-      for (i = 1; i < mp_left; i++)
-        if (*mp[i] < *mp[min])
-          min = i;
-
-      /* Append value to buf2. */
-      *op++ = *mp[min];
-
-      /* Advance merge pointer.
-         Delete this chunk from the set if it's emptied. */
-      if ((++mp[min] - buf1) % CHUNK_SIZE == 0) 
-        mp[min] = mp[--mp_left];
-    }
-}
-
-static void
-verify (void) 
-{
-  size_t buf_idx;
-  size_t hist_idx;
-
-  msg ("verify");
-
-  buf_idx = 0;
-  for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
-       hist_idx++)
-    {
-      while (histogram[hist_idx]-- > 0) 
-        {
-          if (buf2[buf_idx] != hist_idx)
-            fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
-          buf_idx++;
-        } 
-    }
-
-  msg ("success, buf_idx=%'zu", buf_idx);
-}
+#include "tests/vm/parallel-merge.h"
 
 void
-test_main (void)
+test_main (void) 
 {
-  init ();
-  sort_chunks ();
-  merge ();
-  verify ();
+  parallel_merge ("child-sort", 123);
 }
diff --git a/src/tests/vm/page-merge-stk.c b/src/tests/vm/page-merge-stk.c
new file mode 100644 (file)
index 0000000..5eb1069
--- /dev/null
@@ -0,0 +1,8 @@
+#include "tests/main.h"
+#include "tests/vm/parallel-merge.h"
+
+void
+test_main (void) 
+{
+  parallel_merge ("child-qsort", 72);
+}
diff --git a/src/tests/vm/page-merge-stk.ck b/src/tests/vm/page-merge-stk.ck
new file mode 100644 (file)
index 0000000..c5bc1ae
--- /dev/null
@@ -0,0 +1,29 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-stk) begin
+(page-merge-stk) init
+(page-merge-stk) sort chunk 0
+(page-merge-stk) sort chunk 1
+(page-merge-stk) sort chunk 2
+(page-merge-stk) sort chunk 3
+(page-merge-stk) sort chunk 4
+(page-merge-stk) sort chunk 5
+(page-merge-stk) sort chunk 6
+(page-merge-stk) sort chunk 7
+(page-merge-stk) wait for child 0
+(page-merge-stk) wait for child 1
+(page-merge-stk) wait for child 2
+(page-merge-stk) wait for child 3
+(page-merge-stk) wait for child 4
+(page-merge-stk) wait for child 5
+(page-merge-stk) wait for child 6
+(page-merge-stk) wait for child 7
+(page-merge-stk) merge
+(page-merge-stk) verify
+(page-merge-stk) success, buf_idx=1,048,576
+(page-merge-stk) end
+EOF
+pass;
diff --git a/src/tests/vm/parallel-merge.c b/src/tests/vm/parallel-merge.c
new file mode 100644 (file)
index 0000000..cc09bb1
--- /dev/null
@@ -0,0 +1,149 @@
+/* Generates about 1 MB of random data that is then divided into
+   16 chunks.  A separate subprocess sorts each chunk; the
+   subprocesses run in parallel.  Then we merge the chunks and
+   verify that the result is what it should be. */
+
+#include "tests/vm/parallel-merge.h"
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/arc4.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define CHUNK_SIZE (128 * 1024)
+#define CHUNK_CNT 8                             /* Number of chunks. */
+#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE)      /* Buffer size. */
+
+unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
+size_t histogram[256];
+
+/* Initialize buf1 with random data,
+   then count the number of instances of each value within it. */
+static void
+init (void) 
+{
+  struct arc4 arc4;
+  size_t i;
+
+  msg ("init");
+
+  arc4_init (&arc4, "foobar", 6);
+  arc4_crypt (&arc4, buf1, sizeof buf1);
+  for (i = 0; i < sizeof buf1; i++)
+    histogram[buf1[i]]++;
+}
+
+/* Sort each chunk of buf1 using SUBPROCESS,
+   which is expected to return EXIT_STATUS. */
+static void
+sort_chunks (const char *subprocess, int exit_status)
+{
+  pid_t children[CHUNK_CNT];
+  size_t i;
+
+  for (i = 0; i < CHUNK_CNT; i++) 
+    {
+      char fn[128];
+      char cmd[128];
+      int handle;
+
+      msg ("sort chunk %zu", i);
+
+      /* Write this chunk to a file. */
+      snprintf (fn, sizeof fn, "buf%zu", i);
+      create (fn, CHUNK_SIZE);
+      quiet = true;
+      CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
+      write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+      close (handle);
+
+      /* Sort with subprocess. */
+      snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn);
+      CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd);
+      quiet = false;
+    }
+
+  for (i = 0; i < CHUNK_CNT; i++) 
+    {
+      char fn[128];
+      int handle;
+
+      CHECK (wait (children[i]) == exit_status, "wait for child %zu", i);
+
+      /* Read chunk back from file. */
+      quiet = true;
+      snprintf (fn, sizeof fn, "buf%zu", i);
+      CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
+      read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+      close (handle);
+      quiet = false;
+    }
+}
+
+/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
+static void
+merge (void) 
+{
+  unsigned char *mp[CHUNK_CNT];
+  size_t mp_left;
+  unsigned char *op;
+  size_t i;
+
+  msg ("merge");
+
+  /* Initialize merge pointers. */
+  mp_left = CHUNK_CNT;
+  for (i = 0; i < CHUNK_CNT; i++)
+    mp[i] = buf1 + CHUNK_SIZE * i;
+
+  /* Merge. */
+  op = buf2;
+  while (mp_left > 0) 
+    {
+      /* Find smallest value. */
+      size_t min = 0;
+      for (i = 1; i < mp_left; i++)
+        if (*mp[i] < *mp[min])
+          min = i;
+
+      /* Append value to buf2. */
+      *op++ = *mp[min];
+
+      /* Advance merge pointer.
+         Delete this chunk from the set if it's emptied. */
+      if ((++mp[min] - buf1) % CHUNK_SIZE == 0) 
+        mp[min] = mp[--mp_left];
+    }
+}
+
+static void
+verify (void) 
+{
+  size_t buf_idx;
+  size_t hist_idx;
+
+  msg ("verify");
+
+  buf_idx = 0;
+  for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
+       hist_idx++)
+    {
+      while (histogram[hist_idx]-- > 0) 
+        {
+          if (buf2[buf_idx] != hist_idx)
+            fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
+          buf_idx++;
+        } 
+    }
+
+  msg ("success, buf_idx=%'zu", buf_idx);
+}
+
+void
+parallel_merge (const char *child_name, int exit_status)
+{
+  init ();
+  sort_chunks (child_name, exit_status);
+  merge ();
+  verify ();
+}
diff --git a/src/tests/vm/parallel-merge.h b/src/tests/vm/parallel-merge.h
new file mode 100644 (file)
index 0000000..a6b6431
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef TESTS_VM_PARALLEL_MERGE
+#define TESTS_VM_PARALLEL_MERGE 1
+
+void parallel_merge (const char *child_name, int exit_status);
+
+#endif /* tests/vm/parallel-merge.h */
diff --git a/src/tests/vm/qsort.c b/src/tests/vm/qsort.c
new file mode 100644 (file)
index 0000000..922572c
--- /dev/null
@@ -0,0 +1,136 @@
+#include "tests/vm/qsort.h"
+#include <stdbool.h>
+#include <debug.h>
+#include <random.h>
+
+/* Picks a pivot for the quicksort from the SIZE bytes in BUF. */
+static unsigned char
+pick_pivot (unsigned char *buf, size_t size) 
+{
+  ASSERT (size >= 1);
+  return buf[random_ulong () % size];
+}
+
+/* Checks whether the SIZE bytes in ARRAY are divided into an
+   initial LEFT_SIZE elements all less than PIVOT followed by
+   SIZE - LEFT_SIZE elements all greater than or equal to
+   PIVOT. */
+static bool
+is_partitioned (const unsigned char *array, size_t size,
+                unsigned char pivot, size_t left_size) 
+{
+  size_t i;
+  
+  for (i = 0; i < left_size; i++)
+    if (array[i] >= pivot)
+      return false;
+
+  for (; i < size; i++)
+    if (array[i] < pivot)
+      return false;
+
+  return true;
+}
+
+/* Swaps the bytes at *A and *B. */
+static void
+swap (unsigned char *a, unsigned char *b) 
+{
+  unsigned char t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Partitions ARRAY in-place in an initial run of bytes all less
+   than PIVOT, followed by a run of bytes all greater than or
+   equal to PIVOT.  Returns the length of the initial run. */
+static size_t
+partition (unsigned char *array, size_t size, int pivot) 
+{
+  size_t left_size = size;
+  unsigned char *first = array;
+  unsigned char *last = first + left_size;
+
+  for (;;)
+    {
+      /* Move FIRST forward to point to first element greater than
+         PIVOT. */
+      for (;;)
+        {
+          if (first == last)
+            {
+              ASSERT (is_partitioned (array, size, pivot, left_size));
+              return left_size;
+            }
+          else if (*first >= pivot)
+            break;
+
+          first++;
+        }
+      left_size--;
+
+      /* Move LAST backward to point to last element no bigger
+         than PIVOT. */
+      for (;;)
+        {
+          last--;
+
+          if (first == last)
+            {
+              ASSERT (is_partitioned (array, size, pivot, left_size));
+              return left_size;
+            }
+          else if (*last < pivot)
+            break;
+          else
+            left_size--;
+        }
+
+      /* By swapping FIRST and LAST we extend the starting and
+         ending sequences that pass and fail, respectively,
+         PREDICATE. */
+      swap (first, last);
+      first++;
+    }
+}
+
+/* Returns true if the SIZE bytes in BUF are in nondecreasing
+   order, false otherwise. */
+static bool
+is_sorted (const unsigned char *buf, size_t size) 
+{
+  size_t i;
+
+  for (i = 1; i < size; i++)
+    if (buf[i - 1] > buf[i])
+      return false;
+
+  return true;
+}
+
+/* Sorts the SIZE bytes in BUF into nondecreasing order, using
+   the quick-sort algorithm. */
+void
+qsort_bytes (unsigned char *buf, size_t size) 
+{
+  if (!is_sorted (buf, size)) 
+    {
+      int pivot = pick_pivot (buf, size);
+
+      unsigned char *left_half = buf;
+      size_t left_size = partition (buf, size, pivot);
+      unsigned char *right_half = left_half + left_size;
+      size_t right_size = size - left_size;
+  
+      if (left_size <= right_size) 
+        {
+          qsort_bytes (left_half, left_size);
+          qsort_bytes (right_half, right_size); 
+        }
+      else
+        {
+          qsort_bytes (right_half, right_size); 
+          qsort_bytes (left_half, left_size);
+        }
+    } 
+}
diff --git a/src/tests/vm/qsort.h b/src/tests/vm/qsort.h
new file mode 100644 (file)
index 0000000..61b65f3
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef TESTS_VM_QSORT_H
+#define TESTS_VM_QSORT_H 1
+
+#include <stddef.h>
+
+void qsort_bytes (unsigned char *buf, size_t size);
+
+#endif /* tests/vm/qsort.h */