public static void main(String[] args) throws Throwable {

    for (Boolean hasSM : new boolean[] {false, true}) {
      if (hasSM) System.setSecurityManager(new PermissiveSecurityManger());
      for (Charset cs : Charset.availableCharsets().values()) {
        if ("ISO-2022-CN".equals(cs.name())
            || "x-COMPOUND_TEXT".equals(cs.name())
            || "x-JISAutoDetect".equals(cs.name())) continue;
        System.out.printf("Testing(sm=%b) " + cs.name() + "....", hasSM);
        // full bmp first
        char[] bmpCA = new char[0x10000];
        for (int i = 0; i < 0x10000; i++) {
          bmpCA[i] = (char) i;
        }
        byte[] sbBA = new byte[0x100];
        for (int i = 0; i < 0x100; i++) {
          sbBA[i] = (byte) i;
        }
        test(cs, bmpCA, sbBA);
        // "randomed" sizes
        Random rnd = new Random();
        for (int i = 0; i < 10; i++) {
          int clen = rnd.nextInt(0x10000);
          int blen = rnd.nextInt(0x100);
          // System.out.printf("    blen=%d, clen=%d%n", blen, clen);
          test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen));
          // add a pair of surrogates
          int pos = clen / 2;
          if ((pos + 1) < blen) {
            bmpCA[pos] = '\uD800';
            bmpCA[pos + 1] = '\uDC00';
          }
          test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen));
        }

        testMixed(cs);
        System.out.println("done!");
      }
    }
  }
  public static void main(String[] args) throws Throwable {
    final int itrs = Integer.getInteger("iterations", 100000);
    // final int itrs = Integer.getInteger("iterations", 12);
    final int size = Integer.getInteger("size", 2048);
    final int subsize = Integer.getInteger("subsize", 128);
    final int maxchar = Integer.getInteger("maxchar", 128);
    final String regex = System.getProperty("filter");
    final Pattern filter = (regex == null) ? null : Pattern.compile(regex);
    final boolean useSecurityManager = Boolean.getBoolean("SecurityManager");
    if (useSecurityManager) System.setSecurityManager(new PermissiveSecurityManger());
    final Random rnd = new Random();

    String[] csns =
        new String[] {
          "Big5",
          "Johab",
          "EUC_CN",
          "EUC_KR",
          "MS932",
          "MS936",
          "MS949",
          "MS950",
          "GBK",
          "Big5_HKSCS",
          "Big5_HKSCS_2001",
          "Big5_Solaris",
          "MS950_HKSCS",
          "MS950_HKSCS_XP",
          "IBM1364",
          "IBM1381",
          "IBM1383",
          "IBM930",
          "IBM933",
          "IBM935",
          "IBM937",
          "IBM939",
          "IBM942",
          "IBM943",
          "IBM948",
          "IBM949",
          "IBM950",
          "IBM970",
        };

    ArrayList<long[]> sum = new ArrayList<>();

    for (final String csn : csns) {
      final Charset cs = Charset.forName(csn);
      List<Integer> cps = new ArrayList<>(0x4000);
      int off = 0;
      int cp = 0;
      int n = 0;
      CharsetEncoder enc = cs.newEncoder();
      while (cp < 0x10000 && n < cps.size()) {
        if (enc.canEncode((char) cp)) {
          cps.add(cp);
          n++;
        }
        cp++;
      }
      Collections.shuffle(cps);
      char[] ca = new char[cps.size()];
      for (int i = 0; i < cps.size(); i++) ca[i] = (char) (int) cps.get(i);

      System.out.printf("%n--------%s---------%n", csn);
      for (int sz = 8; sz <= 2048; sz *= 2) {
        System.out.printf("   [len=%d]%n", sz);

        final char[] chars = Arrays.copyOf(ca, sz);
        final String str = new String(chars);
        final byte[] bs = str.getBytes(cs);

        Job[] jobs = {
          new Job("String decode: csn") {
            public void work() throws Throwable {
              for (int i = 0; i < itrs; i++) new String(bs, csn);
            }
          },
          new Job("String decode: cs") {
            public void work() throws Throwable {
              for (int i = 0; i < itrs; i++) new String(bs, cs);
            }
          },
          new Job("String encode: csn") {
            public void work() throws Throwable {
              for (int i = 0; i < itrs; i++) str.getBytes(csn);
            }
          },
          new Job("String encode: cs") {
            public void work() throws Throwable {
              for (int i = 0; i < itrs; i++) str.getBytes(cs);
            }
          },
        };
        sum.add(time(jobs));
      }
    }
  }