public static void writeUTF8StringAsJSON(byte[] b, int s, int l, OutputStream os)
      throws IOException {
    int utfLength = UTF8StringUtil.getUTFLength(b, s);
    int position =
        s
            + UTF8StringUtil.getNumBytesToStoreLength(
                utfLength); // skip 2 bytes containing string size
    int maxPosition = position + utfLength;
    os.write('"');
    while (position < maxPosition) {
      char c = UTF8StringUtil.charAt(b, position);
      int sz = UTF8StringUtil.charSize(b, position);
      switch (c) {
          // escape
        case '\b':
          os.write('\\');
          os.write('b');
          position += sz;
          break;
        case '\f':
          os.write('\\');
          os.write('f');
          position += sz;
          break;
        case '\n':
          os.write('\\');
          os.write('n');
          position += sz;
          break;
        case '\r':
          os.write('\\');
          os.write('r');
          position += sz;
          break;
        case '\t':
          os.write('\\');
          os.write('t');
          position += sz;
          break;
        case '\\':
        case '"':
          os.write('\\');
        default:
          switch (sz) {
            case 1:
              if (c <= (byte) 0x1f || c == (byte) 0x7f) {
                // this is to print out "control code points" (single byte UTF-8 representation,
                // value up to 0x1f or 0x7f) in the 'uXXXX' format
                writeUEscape(os, c);
                ++position;
                sz = 0; // no more processing
              }
              break;

            case 2:
              // 2-byte encodings of some code points in modified UTF-8 as described in
              // DataInput.html#modified-utf-8
              //
              //         110xxxxx  10xxxxxx
              // U+0000     00000    000000   C0 80
              // U+0080     00010    000000   C2 80
              // U+009F     00010    011111   C2 9F
              switch (b[position]) {
                case (byte) 0xc0:
                  if (b[position + 1] == (byte) 0x80) {
                    // special treatment for the U+0000 code point as described in
                    // DataInput.html#modified-utf-8
                    writeUEscape(os, c);
                    position += 2;
                    sz = 0; // no more processing
                  }
                  break;
                case (byte) 0xc2:
                  if (b[position + 1] <= (byte) 0x9f) {
                    // special treatment for the U+0080 to U+009F code points
                    writeUEscape(os, c);
                    position += 2;
                    sz = 0; // no more processing
                  }
                  break;
              }
              break;
          }
          while (sz > 0) {
            os.write(b[position]);
            ++position;
            --sz;
          }
          break;
      }
    }
    os.write('\"');
  }