changeset 14544:b31f574254bd

Merge
author amurillo
date Mon, 09 Nov 2015 20:37:05 -0800
parents f3b72beef927 6ed36991e804
children d533cb0187e7
files
diffstat 168 files changed, 10835 insertions(+), 1856 deletions(-) [+]
line wrap: on
line diff
--- a/make/Import.gmk	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/Import.gmk	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -45,8 +45,16 @@
 #
 # Import hotspot
 #
+
+# Don't import jsig library for static builds
+ifneq ($(STATIC_BUILD), true)
+  JSIG_IMPORT = jsig.*
+else
+  JSIG_IMPORT = 
+endif
+
 HOTSPOT_BASE_IMPORT_FILES := \
-    $(addprefix $(LIBRARY_PREFIX), jvm.* jsig.* jvm_db.* jvm_dtrace.*) \
+    $(addprefix $(LIBRARY_PREFIX), jvm.* $(JSIG_IMPORT) jvm_db.* jvm_dtrace.*) \
     Xusage.txt \
     #
 
@@ -79,32 +87,34 @@
 
 ################################################################################
 
-ifeq ($(OPENJDK_TARGET_OS), macosx)
-  JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig$(SHARED_LIBRARY_SUFFIX).dSYM) \
-      $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) )
-else
-  JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.debuginfo) \
-      $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) )
-endif
+ifneq ($(STATIC_BUILD), true)
+  ifeq ($(OPENJDK_TARGET_OS), macosx)
+    JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig$(SHARED_LIBRARY_SUFFIX).dSYM) \
+        $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) )
+  else
+    JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.debuginfo) \
+        $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) )
+  endif
 
-ifneq ($(OPENJDK_TARGET_OS), windows)
-  ifeq ($(JVM_VARIANT_SERVER), true)
-    BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX)
-    ifneq (, $(JSIG_DEBUGINFO))
-      BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I))
+  ifneq ($(OPENJDK_TARGET_OS), windows)
+    ifeq ($(JVM_VARIANT_SERVER), true)
+      BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX)
+      ifneq (, $(JSIG_DEBUGINFO))
+        BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I))
+      endif
     endif
-  endif
-  ifeq ($(JVM_VARIANT_CLIENT), true)
-    BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX)
-    ifneq (, $(JSIG_DEBUGINFO))
-      BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I))
+    ifeq ($(JVM_VARIANT_CLIENT), true)
+      BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX)
+      ifneq (, $(JSIG_DEBUGINFO))
+        BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I))
+      endif
     endif
-  endif
-  ifneq ($(OPENJDK_TARGET_OS), macosx)
-    ifeq ($(JVM_VARIANT_MINIMAL1), true)
-      BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX)
-      ifneq (,$(JSIG_DEBUGINFO))
-        BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I))
+    ifneq ($(OPENJDK_TARGET_OS), macosx)
+      ifeq ($(JVM_VARIANT_MINIMAL1), true)
+        BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX)
+        ifneq (,$(JSIG_DEBUGINFO))
+          BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I))
+        endif
       endif
     endif
   endif
--- a/make/data/charsetmapping/DoubleByte-X.java.template	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/data/charsetmapping/DoubleByte-X.java.template	Mon Nov 09 20:37:05 2015 -0800
@@ -50,12 +50,12 @@
 
     public CharsetDecoder newDecoder() {
         initb2c();
-        return new  DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$);
+        return new  DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$, $ASCIICOMPATIBLE$);
     }
 
     public CharsetEncoder newEncoder() {
         initc2b();
-        return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex); 
+        return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex, $ASCIICOMPATIBLE$);
     }
 
     $B2C$
--- a/make/data/charsetmapping/SingleByte-X.java.template	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/data/charsetmapping/SingleByte-X.java.template	Mon Nov 09 20:37:05 2015 -0800
@@ -48,11 +48,11 @@
     }
 
     public CharsetDecoder newDecoder() {
-        return new SingleByte.Decoder(this, b2c);
+        return new SingleByte.Decoder(this, b2c, $ASCIICOMPATIBLE$);
     }
 
     public CharsetEncoder newEncoder() {
-        return new SingleByte.Encoder(this, c2b, c2bIndex);
+        return new SingleByte.Encoder(this, c2b, c2bIndex, $ASCIICOMPATIBLE$);
     }
 
     private final static String b2cTable = $B2CTABLE$
--- a/make/launcher/LauncherCommon.gmk	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/launcher/LauncherCommon.gmk	Mon Nov 09 20:37:05 2015 -0800
@@ -122,8 +122,23 @@
     endif
 
     $1_CFLAGS += -DPACKAGE_PATH='"$(PACKAGE_PATH)"'
-    $1_LDFLAGS += -Wl,-all_load $(SUPPORT_OUTPUTDIR)/native/java.base/libjli_static.a \
+    $1_LDFLAGS += -Wl,-all_load \
         -sectcreate __TEXT __info_plist $(MACOSX_PLIST_DIR)/$$($1_PLIST_FILE)
+    ifeq ($(STATIC_BUILD), true)
+      $1_LDFLAGS += -exported_symbols_list \
+              $(SUPPORT_OUTPUTDIR)/build-static/exported.symbols
+      $1_LIBS += \
+          $(shell $(FIND) $(SUPPORT_OUTPUTDIR)/modules_libs/java.base -name "*.a") \
+          $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/libdt_socket.a \
+          $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/libjdwp.a \
+          $(SUPPORT_OUTPUTDIR)/native/java.base/$(LIBRARY_PREFIX)fdlibm$(STATIC_LIBRARY_SUFFIX) \
+          -framework CoreFoundation \
+          -framework Foundation \
+          -framework SystemConfiguration \
+          -lstdc++ -liconv
+    else
+      $1_LIBS += $(SUPPORT_OUTPUTDIR)/native/java.base/libjli_static.a
+    endif
     $1_LIBS += -framework Cocoa -framework Security \
         -framework ApplicationServices
   endif
--- a/make/lib/CoreLibraries.gmk	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/lib/CoreLibraries.gmk	Mon Nov 09 20:37:05 2015 -0800
@@ -435,10 +435,14 @@
       OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static, \
       DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES)))
 
-  $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a: $(BUILD_LIBJLI_STATIC)
+  ifeq ($(STATIC_BUILD), true)
+    TARGETS += $(BUILD_LIBJLI_STATIC)
+  else
+    $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a: $(BUILD_LIBJLI_STATIC)
 	$(call install-file)
 
-  TARGETS += $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a
+    TARGETS += $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a
+  endif
 
 else ifeq ($(OPENJDK_TARGET_OS), aix)
   # AIX also requires a static libjli because the compiler doesn't support '-rpath'
--- a/make/lib/Lib-java.base.gmk	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/lib/Lib-java.base.gmk	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -33,3 +33,29 @@
 include NetworkingLibraries.gmk
 include NioLibraries.gmk
 include SecurityLibraries.gmk
+
+ifeq ($(STATIC_BUILD), true)
+  JAVA_BASE_EXPORT_SYMBOLS_SRC := \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/jli/$(LIBRARY_PREFIX)jli.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)java.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)net.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)nio.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)verify.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)zip.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)jimage.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/server/$(LIBRARY_PREFIX)jvm.symbols \
+      #
+
+  JAVA_BASE_EXPORT_SYMBOL_FILE := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/java.base.symbols
+
+  $(JAVA_BASE_EXPORT_SYMBOL_FILE): $(JAVA_BASE_EXPORT_SYMBOLS_SRC)
+	$(ECHO) $(LOG_INFO) "Generating java.base.symbols file"
+	$(CAT) $^ > $@
+
+  # The individual symbol files is generated when the respective lib is built
+  $(JAVA_BASE_EXPORT_SYMBOLS_SRC): $(BUILD_LIBJLI) $(BUILD_LIBJAVA) \
+      $(BUILD_LIBNET) $(BUILD_LIBNIO) $(BUILD_LIBVERIFY) $(BUILD_LIBZIP) \
+      $(BUILD_LIBJIMAGE)
+
+  TARGETS += $(JAVA_BASE_EXPORT_SYMBOL_FILE)
+endif
--- a/make/lib/Lib-jdk.jdwp.agent.gmk	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/lib/Lib-jdk.jdwp.agent.gmk	Mon Nov 09 20:37:05 2015 -0800
@@ -102,3 +102,23 @@
 TARGETS += $(BUILD_LIBJDWP)
 
 ################################################################################
+
+ifeq ($(STATIC_BUILD), true)
+  JDK_JDWP_AGENT_EXPORT_SYMBOLS_SRC := \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/$(LIBRARY_PREFIX)dt_socket.symbols \
+      $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/$(LIBRARY_PREFIX)jdwp.symbols
+
+  JDK_JDWP_AGENT_EXPORT_SYMBOL_FILE := $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/jdk.jdwp.agent.symbols
+
+  $(JDK_JDWP_AGENT_EXPORT_SYMBOL_FILE): $(JDK_JDWP_AGENT_EXPORT_SYMBOLS_SRC)
+	$(ECHO) $(LOG_INFO) "Generating jdk.jdwp.agent symbols file"
+	$(CAT) $^ > $@
+
+  # The individual symbol files is generated when the respective lib is built
+  $(JDK_JDWP_AGENT_EXPORT_SYMBOLS_SRC): $(BUILD_LIBDT_SOCKET) $(BUILD_LIBJDWP)
+
+  TARGETS += $(JDK_JDWP_AGENT_EXPORT_SYMBOL_FILE)
+
+endif
+
+################################################################################
--- a/make/lib/SecurityLibraries.gmk	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/lib/SecurityLibraries.gmk	Mon Nov 09 20:37:05 2015 -0800
@@ -26,38 +26,41 @@
 include LibCommon.gmk
 
 ifeq ($(OPENJDK_TARGET_OS), macosx)
+  # JavaNativeFoundation framework not supported in static builds 
+  ifneq ($(STATIC_BUILD), true)
 
   ################################################################################
 
-  LIBOSXSECURITY_DIRS := $(JDK_TOPDIR)/src/java.base/macosx/native/libosxsecurity
-  LIBOSXSECURITY_CFLAGS := -I$(LIBOSXSECURITY_DIRS) \
-      $(LIBJAVA_HEADER_FLAGS) \
-      -I$(SUPPORT_OUTPUTDIR)/headers/java.base \
+    LIBOSXSECURITY_DIRS := $(JDK_TOPDIR)/src/java.base/macosx/native/libosxsecurity
+    LIBOSXSECURITY_CFLAGS := -I$(LIBOSXSECURITY_DIRS) \
+        $(LIBJAVA_HEADER_FLAGS) \
+        -I$(SUPPORT_OUTPUTDIR)/headers/java.base \
 
-  $(eval $(call SetupNativeCompilation,BUILD_LIBOSXSECURITY, \
-      LIBRARY := osxsecurity, \
-      OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
-      SRC := $(LIBOSXSECURITY_DIRS), \
-      OPTIMIZATION := LOW, \
-      CFLAGS := $(CFLAGS_JDKLIB) \
-          $(LIBOSXSECURITY_CFLAGS), \
-      DISABLED_WARNINGS_clang := deprecated-declarations, \
-      LDFLAGS := $(LDFLAGS_JDKLIB) \
-          -L$(SUPPORT_OUTPUTDIR)/modules_libs/java.base \
-          $(call SET_SHARED_LIBRARY_ORIGIN) \
-          -fobjc-link-runtime, \
-      LIBS := \
-          -framework JavaNativeFoundation \
-          -framework CoreServices \
-          -framework Security \
-          $(JDKLIB_LIBS), \
-      OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxsecurity, \
-      DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES)))
+    $(eval $(call SetupNativeCompilation,BUILD_LIBOSXSECURITY, \
+        LIBRARY := osxsecurity, \
+        OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
+        SRC := $(LIBOSXSECURITY_DIRS), \
+        OPTIMIZATION := LOW, \
+        CFLAGS := $(CFLAGS_JDKLIB) \
+            $(LIBOSXSECURITY_CFLAGS), \
+        DISABLED_WARNINGS_clang := deprecated-declarations, \
+        LDFLAGS := $(LDFLAGS_JDKLIB) \
+            -L$(SUPPORT_OUTPUTDIR)/modules_libs/java.base \
+            $(call SET_SHARED_LIBRARY_ORIGIN) \
+            -fobjc-link-runtime, \
+        LIBS := \
+            -framework JavaNativeFoundation \
+            -framework CoreServices \
+            -framework Security \
+            $(JDKLIB_LIBS), \
+        OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxsecurity, \
+        DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES)))
 
-  $(BUILD_LIBOSXSECURITY): $(BUILD_LIBJAVA)
+    $(BUILD_LIBOSXSECURITY): $(BUILD_LIBJAVA)
 
-  TARGETS += $(BUILD_LIBOSXSECURITY)
+    TARGETS += $(BUILD_LIBOSXSECURITY)
 
   ################################################################################
 
+  endif
 endif
--- a/make/mapfiles/libjava/mapfile-vers	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/mapfiles/libjava/mapfile-vers	Mon Nov 09 20:37:05 2015 -0800
@@ -211,6 +211,7 @@
 		Java_java_lang_SecurityManager_getClassContext;
 		Java_java_lang_Shutdown_halt0;
 		Java_java_lang_String_intern;
+		Java_java_lang_StringUTF16_isBigEndian;
 		Java_java_lang_System_identityHashCode;
 		Java_java_lang_System_initProperties;
 		Java_java_lang_System_mapLibraryName;
--- a/make/mapfiles/libjava/reorder-sparc	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/mapfiles/libjava/reorder-sparc	Mon Nov 09 20:37:05 2015 -0800
@@ -57,6 +57,7 @@
 text: .text%JNU_ClassString;
 text: .text%JNU_CopyObjectArray;
 text: .text%Java_java_lang_String_intern;
+text: .text%Java_java_lang_StringUTF16_isBigEndian;
 text: .text%Java_java_lang_ClassLoader_findLoadedClass0;
 text: .text%Java_java_lang_ClassLoader_findBootstrapClass;
 text: .text%Java_java_lang_Throwable_fillInStackTrace;
--- a/make/mapfiles/libjava/reorder-sparcv9	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/mapfiles/libjava/reorder-sparcv9	Mon Nov 09 20:37:05 2015 -0800
@@ -29,6 +29,7 @@
 text: .text%Java_sun_reflect_Reflection_getCallerClass__I;
 text: .text%Java_java_lang_Class_forName0;
 text: .text%Java_java_lang_String_intern;
+text: .text%Java_java_lang_StringUTF16_isBigEndian;
 text: .text%Java_java_lang_Float_floatToRawIntBits;
 text: .text%Java_java_lang_Double_doubleToRawLongBits;
 text: .text%Java_java_lang_ClassLoader_findLoadedClass0;
--- a/make/mapfiles/libjava/reorder-x86	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/mapfiles/libjava/reorder-x86	Mon Nov 09 20:37:05 2015 -0800
@@ -31,6 +31,7 @@
 text: .text%Java_sun_reflect_Reflection_getCallerClass__I;
 text: .text%Java_java_lang_Class_forName0;
 text: .text%Java_java_lang_String_intern;
+text: .text%Java_java_lang_StringUTF16_isBigEndian;
 text: .text%Java_sun_reflect_NativeConstructorAccessorImpl_newInstance0;
 text: .text%Java_java_lang_Throwable_fillInStackTrace;
 text: .text%Java_java_lang_System_setOut0;
--- a/make/mapfiles/libnio/mapfile-macosx	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/mapfiles/libnio/mapfile-macosx	Mon Nov 09 20:37:05 2015 -0800
@@ -75,6 +75,7 @@
                 Java_sun_nio_ch_IOUtil_makePipe;
                 Java_sun_nio_ch_IOUtil_randomBytes;
                 Java_sun_nio_ch_IOUtil_setfdVal;
+                Java_sun_nio_ch_IOUtil_iovMax;
 		Java_sun_nio_ch_KQueue_kqueue;
 		Java_sun_nio_ch_KQueue_keventRegister;
 		Java_sun_nio_ch_KQueue_keventPoll;
--- a/make/src/classes/build/tools/charsetmapping/DBCS.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/src/classes/build/tools/charsetmapping/DBCS.java	Mon Nov 09 20:37:05 2015 -0800
@@ -197,6 +197,7 @@
                        .replace("$B1MAX$"   , "0x" + Integer.toString(b1Max, 16))
                        .replace("$B2MIN$"   , "0x" + Integer.toString(b2Min, 16))
                        .replace("$B2MAX$"   , "0x" + Integer.toString(b2Max, 16))
+                       .replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false")
                        .replace("$B2C$", b2c)
                        .replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16))
                        .replace("$NONROUNDTRIP_B2C$", b2cNR)
--- a/make/src/classes/build/tools/charsetmapping/SBCS.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/make/src/classes/build/tools/charsetmapping/SBCS.java	Mon Nov 09 20:37:05 2015 -0800
@@ -175,6 +175,9 @@
                 else
                     line = "        return (cs instanceof " + clzName + ");";
             }
+            if (line.indexOf("$ASCIICOMPATIBLE$") != -1) {
+                line = line.replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false");
+            }
             if (line.indexOf("$B2CTABLE$") != -1) {
                 line = line.replace("$B2CTABLE$", b2c);
             }
--- a/src/demo/share/jvmti/agent_util/agent_util.h	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/agent_util/agent_util.h	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -65,6 +65,51 @@
 void *allocate(jvmtiEnv *jvmti, jint len);
 void  add_demo_jar_to_bootclasspath(jvmtiEnv *jvmti, char *demo_name);
 
+#ifdef STATIC_BUILD
+/* Macros for handling declaration of static/dynamic
+ * Agent library Load/Attach/Unload functions
+ *
+ * DEF_Agent_OnLoad, DEF_Agent_OnAttach or DEF_Agent_OnUnload
+ * generate the appropriate entrypoint names based on static
+ * versus dynamic builds.
+ *
+ * STATIC_BUILD must be defined to build static versions of these libraries.
+ * LIBRARY_NAME must be set to the name of the library for static builds.
+ */
+#define ADD_LIB_NAME3(name, lib) name ## lib
+#define ADD_LIB_NAME2(name, lib) ADD_LIB_NAME3(name, lib)
+#define ADD_LIB_NAME(entry) ADD_LIB_NAME2(entry, LIBRARY_NAME)
+
+#define DEF_Agent_OnLoad \
+ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) \
+{ \
+  jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_)(JavaVM *vm, char *options, void *reserved); \
+  return ADD_LIB_NAME(Agent_OnLoad_dynamic_)(vm, options, reserved); \
+} \
+jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_)
+
+#define DEF_Agent_OnAttach \
+ADD_LIB_NAME(Agent_OnAttach_)(JavaVM *vm, char *options, void *reserved) \
+{ \
+  jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_)(JavaVM *vm, char *options, void *reserved); \
+  return ADD_LIB_NAME(Agent_OnAttach_dynamic_)(vm, options, reserved); \
+} \
+jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_)
+
+#define DEF_Agent_OnUnload \
+ADD_LIB_NAME(Agent_OnUnload_)(JavaVM *vm) \
+{ \
+  void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_)(JavaVM *vm); \
+  ADD_LIB_NAME(Agent_OnUnload_dynamic_)(vm); \
+} \
+void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_)
+
+#else
+#define DEF_Agent_OnLoad Agent_OnLoad
+#define DEF_Agent_OnAttach Agent_OnAttach
+#define DEF_Agent_OnUnload Agent_OnUnload
+#endif
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif /* __cplusplus */
--- a/src/demo/share/jvmti/compiledMethodLoad/compiledMethodLoad.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/compiledMethodLoad/compiledMethodLoad.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -225,7 +225,7 @@
  * event here.
  */
 JNIEXPORT jint JNICALL
-Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 {
     jint                rc;
     jvmtiError          err;
@@ -272,6 +272,6 @@
 
 /* Agent_OnUnload() is called last */
 JNIEXPORT void JNICALL
-Agent_OnUnload(JavaVM *vm)
+DEF_Agent_OnUnload(JavaVM *vm)
 {
 }
--- a/src/demo/share/jvmti/gctest/gctest.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/gctest/gctest.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -148,7 +148,7 @@
 
 /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */
 JNIEXPORT jint JNICALL
-Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 {
     jint                rc;
     jvmtiError          err;
@@ -193,6 +193,6 @@
 
 /* Agent_OnUnload() is called last */
 JNIEXPORT void JNICALL
-Agent_OnUnload(JavaVM *vm)
+DEF_Agent_OnUnload(JavaVM *vm)
 {
 }
--- a/src/demo/share/jvmti/heapTracker/heapTracker.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/heapTracker/heapTracker.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -894,7 +894,7 @@
  *   loaded. This is the first code executed.
  */
 JNIEXPORT jint JNICALL
-Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 {
     static GlobalAgentData data;
     jvmtiEnv              *jvmti;
@@ -1010,7 +1010,7 @@
  *   unloaded. This is the last code executed.
  */
 JNIEXPORT void JNICALL
-Agent_OnUnload(JavaVM *vm)
+DEF_Agent_OnUnload(JavaVM *vm)
 {
     /* Skip any cleanup, VM is about to die anyway */
 }
--- a/src/demo/share/jvmti/heapTracker/heapTracker.h	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/heapTracker/heapTracker.h	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -64,9 +64,4 @@
 
 #include "agent_util.h"
 
-/* Agent library externals to export. */
-
-JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved);
-JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm);
-
 #endif
--- a/src/demo/share/jvmti/heapViewer/heapViewer.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/heapViewer/heapViewer.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -235,7 +235,7 @@
 
 /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */
 JNIEXPORT jint JNICALL
-Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 {
     jint                rc;
     jvmtiError          err;
@@ -283,6 +283,6 @@
 
 /* Agent_OnUnload() is called last */
 JNIEXPORT void JNICALL
-Agent_OnUnload(JavaVM *vm)
+DEF_Agent_OnUnload(JavaVM *vm)
 {
 }
--- a/src/demo/share/jvmti/minst/minst.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/minst/minst.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -373,7 +373,7 @@
  *   loaded. This is the first code executed.
  */
 JNIEXPORT jint JNICALL
-Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 {
     static GlobalAgentData data;
     jvmtiEnv              *jvmti;
@@ -467,7 +467,7 @@
  *   unloaded. This is the last code executed.
  */
 JNIEXPORT void JNICALL
-Agent_OnUnload(JavaVM *vm)
+DEF_Agent_OnUnload(JavaVM *vm)
 {
     /* Make sure all malloc/calloc/strdup space is freed */
     if ( gdata->include != NULL ) {
--- a/src/demo/share/jvmti/minst/minst.h	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/minst/minst.h	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -64,9 +64,4 @@
 
 #include "agent_util.h"
 
-/* Agent library externals to export. */
-
-JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved);
-JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm);
-
 #endif
--- a/src/demo/share/jvmti/mtrace/mtrace.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/mtrace/mtrace.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -697,7 +697,7 @@
  *   loaded. This is the first code executed.
  */
 JNIEXPORT jint JNICALL
-Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 {
     static GlobalAgentData data;
     jvmtiEnv              *jvmti;
@@ -795,7 +795,7 @@
  *   unloaded. This is the last code executed.
  */
 JNIEXPORT void JNICALL
-Agent_OnUnload(JavaVM *vm)
+DEF_Agent_OnUnload(JavaVM *vm)
 {
     /* Make sure all malloc/calloc/strdup space is freed */
     if ( gdata->include != NULL ) {
--- a/src/demo/share/jvmti/mtrace/mtrace.h	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/mtrace/mtrace.h	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -64,9 +64,4 @@
 
 #include "agent_util.h"
 
-/* Agent library externals to export. */
-
-JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved);
-JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm);
-
 #endif
--- a/src/demo/share/jvmti/versionCheck/versionCheck.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/versionCheck/versionCheck.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -89,7 +89,7 @@
 
 /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */
 JNIEXPORT jint JNICALL
-Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 {
     jint                rc;
     jvmtiError          err;
@@ -116,6 +116,6 @@
 
 /* Agent_OnUnload() is called last */
 JNIEXPORT void JNICALL
-Agent_OnUnload(JavaVM *vm)
+DEF_Agent_OnUnload(JavaVM *vm)
 {
 }
--- a/src/demo/share/jvmti/waiters/Monitor.hpp	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/waiters/Monitor.hpp	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -37,6 +37,10 @@
  * this sample code.
  */
 
+#ifdef STATIC_BUILD
+#define Monitor WaiterMonitor
+#endif
+
 
 /* C++ Monitor class */
 
--- a/src/demo/share/jvmti/waiters/Thread.cpp	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/waiters/Thread.cpp	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -37,6 +37,9 @@
  * this sample code.
  */
 
+#ifdef STATIC_BUILD
+#define Thread WaiterThread
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
--- a/src/demo/share/jvmti/waiters/waiters.cpp	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/demo/share/jvmti/waiters/waiters.cpp	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -243,7 +243,7 @@
 
     /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */
     JNIEXPORT jint JNICALL
-    Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+    DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
     {
         jvmtiEnv           *jvmti;
         jint                rc;
@@ -288,7 +288,7 @@
 
     /* Agent_OnUnload() is called last */
     JNIEXPORT void JNICALL
-    Agent_OnUnload(JavaVM *vm)
+    DEF_Agent_OnUnload(JavaVM *vm)
     {
     }
 
--- a/src/java.base/macosx/native/libjava/java_props_macosx.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/macosx/native/libjava/java_props_macosx.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,13 +35,15 @@
 
 #include "java_props_macosx.h"
 
-
 // need dlopen/dlsym trick to avoid pulling in JavaRuntimeSupport before libjava.dylib is loaded
 static void *getJRSFramework() {
     static void *jrsFwk = NULL;
+#ifndef STATIC_BUILD
+// JavaRuntimeSupport doesn't support static Java runtimes
     if (jrsFwk == NULL) {
        jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL);
     }
+#endif
     return jrsFwk;
 }
 
--- a/src/java.base/macosx/native/libjli/java_md_macosx.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/macosx/native/libjli/java_md_macosx.c	Mon Nov 09 20:37:05 2015 -0800
@@ -245,6 +245,8 @@
     return sExportedJNIFunctions = fxns;
 }
 
+#ifndef STATIC_BUILD
+
 JNIEXPORT jint JNICALL
 JNI_GetDefaultJavaVMInitArgs(void *args) {
     InvocationFunctions *ifn = GetExportedJNIFunctions();
@@ -265,6 +267,7 @@
     if (ifn == NULL) return JNI_ERR;
     return ifn->GetCreatedJavaVMs(vmBuf, bufLen, nVMs);
 }
+#endif
 
 /*
  * Allow JLI-aware launchers to specify a client/server preference
@@ -303,7 +306,12 @@
     objc_registerThreadWithCollector();
 
     if (main_fptr == NULL) {
+#ifdef STATIC_BUILD
+        extern int main(int argc, char **argv);
+        main_fptr = &main;
+#else
         main_fptr = (int (*)())dlsym(RTLD_DEFAULT, "main");
+#endif
         if (main_fptr == NULL) {
             JLI_ReportErrorMessageSys("error locating main entrypoint\n");
             exit(1);
@@ -588,6 +596,9 @@
 
     JLI_TraceLauncher("Does `%s' exist ... ", jvmpath);
 
+#ifdef STATIC_BUILD
+    return JNI_TRUE;
+#else
     if (stat(jvmpath, &s) == 0) {
         JLI_TraceLauncher("yes.\n");
         return JNI_TRUE;
@@ -595,6 +606,7 @@
         JLI_TraceLauncher("no.\n");
         return JNI_FALSE;
     }
+#endif
 }
 
 /*
@@ -607,10 +619,18 @@
 
     if (GetApplicationHome(path, pathsize)) {
         /* Is JRE co-located with the application? */
+#ifdef STATIC_BUILD
+        char jvm_cfg[MAXPATHLEN];
+        JLI_Snprintf(jvm_cfg, sizeof(jvm_cfg), "%s/lib/jvm.cfg", path);
+        if (access(jvm_cfg, F_OK) == 0) {
+            return JNI_TRUE;
+        }
+#else
         JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path);
         if (access(libjava, F_OK) == 0) {
             return JNI_TRUE;
         }
+#endif
         /* ensure storage for path + /jre + NULL */
         if ((JLI_StrLen(path) + 4 + 1) > (size_t) pathsize) {
             JLI_TraceLauncher("Insufficient space to store JRE path\n");
@@ -629,6 +649,24 @@
     Dl_info selfInfo;
     dladdr(&GetJREPath, &selfInfo);
 
+#ifdef STATIC_BUILD
+    char jvm_cfg[MAXPATHLEN];
+    char *p = NULL;
+    strncpy(jvm_cfg, selfInfo.dli_fname, MAXPATHLEN);
+    p = strrchr(jvm_cfg, '/'); *p = '\0';
+    p = strrchr(jvm_cfg, '/');
+    if (strcmp(p, "/.") == 0) {
+      *p = '\0';
+      p = strrchr(jvm_cfg, '/'); *p = '\0';
+    }
+    else *p = '\0';
+    strncpy(path, jvm_cfg, pathsize);
+    strncat(jvm_cfg, "/lib/jvm.cfg", MAXPATHLEN);
+    if (access(jvm_cfg, F_OK) == 0) {
+      return JNI_TRUE;
+    }
+#endif
+
     char *realPathToSelf = realpath(selfInfo.dli_fname, path);
     if (realPathToSelf != path) {
         return JNI_FALSE;
@@ -664,7 +702,11 @@
 
     JLI_TraceLauncher("JVM path is %s\n", jvmpath);
 
+#ifndef STATIC_BUILD
     libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
+#else
+    libjvm = dlopen(NULL, RTLD_FIRST);
+#endif
     if (libjvm == NULL) {
         JLI_ReportErrorMessage(DLL_ERROR1, __LINE__);
         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
@@ -714,9 +756,14 @@
     char* exec_path = NULL;
     {
         Dl_info dlinfo;
+
+#ifdef STATIC_BUILD
+        void *fptr;
+        fptr = (void *)&SetExecname;
+#else
         int (*fptr)();
-
         fptr = (int (*)())dlsym(RTLD_DEFAULT, "main");
+#endif
         if (fptr == NULL) {
             JLI_ReportErrorMessage(DLL_ERROR3, dlerror());
             return JNI_FALSE;
--- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java	Mon Nov 09 20:37:05 2015 -0800
@@ -31,6 +31,12 @@
 import java.util.stream.IntStream;
 import java.util.stream.StreamSupport;
 
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.String.UTF16;
+import static java.lang.String.LATIN1;
+import static java.lang.String.checkIndex;
+import static java.lang.String.checkOffset;
+
 /**
  * A mutable sequence of characters.
  * <p>
@@ -51,7 +57,12 @@
     /**
      * The value is used for character storage.
      */
-    char[] value;
+    byte[] value;
+
+    /**
+     * The id of the encoding used to encode the bytes in {@code value}.
+     */
+    byte coder;
 
     /**
      * The count is the number of characters used.
@@ -68,7 +79,13 @@
      * Creates an AbstractStringBuilder of the specified capacity.
      */
     AbstractStringBuilder(int capacity) {
-        value = new char[capacity];
+        if (COMPACT_STRINGS) {
+            value = new byte[capacity];
+            coder = LATIN1;
+        } else {
+            value = StringUTF16.newBytesFor(capacity);
+            coder = UTF16;
+        }
     }
 
     /**
@@ -90,7 +107,7 @@
      * @return  the current capacity
      */
     public int capacity() {
-        return value.length;
+        return value.length >> coder;
     }
 
     /**
@@ -110,8 +127,9 @@
      * @param   minimumCapacity   the minimum desired capacity.
      */
     public void ensureCapacity(int minimumCapacity) {
-        if (minimumCapacity > 0)
+        if (minimumCapacity > 0) {
             ensureCapacityInternal(minimumCapacity);
+        }
     }
 
     /**
@@ -120,24 +138,48 @@
      */
     private void ensureCapacityInternal(int minimumCapacity) {
         // overflow-conscious code
-        if (minimumCapacity - value.length > 0)
+        int capacity = value.length >> coder;
+        if (minimumCapacity - capacity > 0) {
             expandCapacity(minimumCapacity);
+        }
     }
 
     /**
      * This implements the expansion semantics of ensureCapacity with no
      * size check or synchronization.
      */
-    void expandCapacity(int minimumCapacity) {
-        int newCapacity = value.length * 2 + 2;
-        if (newCapacity - minimumCapacity < 0)
+    private void expandCapacity(int minimumCapacity) {
+        int newCapacity = (value.length >> coder) * 2 + 2;
+        if (newCapacity - minimumCapacity < 0) {
             newCapacity = minimumCapacity;
+        }
         if (newCapacity < 0) {
-            if (minimumCapacity < 0) // overflow
+            if (minimumCapacity < 0) {// overflow
                 throw new OutOfMemoryError();
+            }
             newCapacity = Integer.MAX_VALUE;
         }
-        value = Arrays.copyOf(value, newCapacity);
+        if (coder != LATIN1 && newCapacity > StringUTF16.MAX_LENGTH) {
+            if (minimumCapacity >= StringUTF16.MAX_LENGTH) {
+                throw new OutOfMemoryError();
+            }
+            newCapacity = StringUTF16.MAX_LENGTH;
+        }
+        this.value = Arrays.copyOf(value, newCapacity << coder);
+    }
+
+    /**
+     * If the coder is "isLatin1", this inflates the internal 8-bit storage
+     * to 16-bit <hi=0, low> pair storage.
+     */
+    private void inflate() {
+        if (!isLatin1()) {
+            return;
+        }
+        byte[] buf = StringUTF16.newBytesFor(value.length);
+        StringLatin1.inflateSB(value, buf, 0, count);
+        this.value = buf;
+        this.coder = UTF16;
     }
 
     /**
@@ -148,8 +190,9 @@
      * returned by a subsequent call to the {@link #capacity()} method.
      */
     public void trimToSize() {
-        if (count < value.length) {
-            value = Arrays.copyOf(value, count);
+        int length = count << coder;
+        if (length < value.length) {
+            value = Arrays.copyOf(value, length);
         }
     }
 
@@ -179,14 +222,17 @@
      *               {@code newLength} argument is negative.
      */
     public void setLength(int newLength) {
-        if (newLength < 0)
+        if (newLength < 0) {
             throw new StringIndexOutOfBoundsException(newLength);
+        }
         ensureCapacityInternal(newLength);
-
         if (count < newLength) {
-            Arrays.fill(value, count, newLength, '\0');
+            if (isLatin1()) {
+                StringLatin1.fillNull(value, count, newLength);
+            } else {
+                StringUTF16.fillNull(value, count, newLength);
+            }
         }
-
         count = newLength;
     }
 
@@ -209,9 +255,11 @@
      */
     @Override
     public char charAt(int index) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        return value[index];
+        checkIndex(index, count);
+        if (isLatin1()) {
+            return (char)(value[index] & 0xff);
+        }
+        return StringUTF16.charAt(value, index);
     }
 
     /**
@@ -236,10 +284,11 @@
      *             sequence.
      */
     public int codePointAt(int index) {
-        if ((index < 0) || (index >= count)) {
-            throw new StringIndexOutOfBoundsException(index);
+        checkIndex(index, count);
+        if (isLatin1()) {
+            return value[index] & 0xff;
         }
-        return Character.codePointAtImpl(value, index, count);
+        return StringUTF16.codePointAtSB(value, index, count);
     }
 
     /**
@@ -265,10 +314,13 @@
      */
     public int codePointBefore(int index) {
         int i = index - 1;
-        if ((i < 0) || (i >= count)) {
+        if (i < 0 || i >= count) {
             throw new StringIndexOutOfBoundsException(index);
         }
-        return Character.codePointBeforeImpl(value, index, 0);
+        if (isLatin1()) {
+            return value[i] & 0xff;
+        }
+        return StringUTF16.codePointBeforeSB(value, index);
     }
 
     /**
@@ -295,7 +347,10 @@
         if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
+        if (isLatin1()) {
+            return endIndex - beginIndex;
+        }
+        return StringUTF16.codePointCountSB(value, beginIndex, endIndex);
     }
 
     /**
@@ -321,8 +376,8 @@
         if (index < 0 || index > count) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.offsetByCodePointsImpl(value, 0, count,
-                                                index, codePointOffset);
+        return Character.offsetByCodePoints(this,
+                                            index, codePointOffset);
     }
 
     /**
@@ -355,13 +410,14 @@
      */
     public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
     {
-        if (srcBegin < 0)
-            throw new StringIndexOutOfBoundsException(srcBegin);
-        if ((srcEnd < 0) || (srcEnd > count))
-            throw new StringIndexOutOfBoundsException(srcEnd);
-        if (srcBegin > srcEnd)
-            throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
-        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+        checkRangeSIOOBE(srcBegin, srcEnd, count);  // compatible to old version
+        int n = srcEnd - srcBegin;
+        checkRange(dstBegin, dstBegin + n, dst.length);
+        if (isLatin1()) {
+            StringLatin1.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin);
+        } else {
+            StringUTF16.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin);
+        }
     }
 
     /**
@@ -379,9 +435,15 @@
      *             negative or greater than or equal to {@code length()}.
      */
     public void setCharAt(int index, char ch) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        value[index] = ch;
+        checkIndex(index, count);
+        if (isLatin1() && StringLatin1.canEncode(ch)) {
+            value[index] = (byte)ch;
+        } else {
+            if (isLatin1()) {
+                inflate();
+            }
+            StringUTF16.putCharSB(value, index, ch);
+        }
     }
 
     /**
@@ -418,35 +480,34 @@
      * @return  a reference to this object.
      */
     public AbstractStringBuilder append(String str) {
-        if (str == null)
+        if (str == null) {
             return appendNull();
+        }
         int len = str.length();
         ensureCapacityInternal(count + len);
-        str.getChars(0, len, value, count);
+        putStringAt(count, str);
         count += len;
         return this;
     }
 
     // Documentation in subclasses because of synchro difference
     public AbstractStringBuilder append(StringBuffer sb) {
-        if (sb == null)
-            return appendNull();
-        int len = sb.length();
-        ensureCapacityInternal(count + len);
-        sb.getChars(0, len, value, count);
-        count += len;
-        return this;
+        return this.append((AbstractStringBuilder)sb);
     }
 
     /**
      * @since 1.8
      */
     AbstractStringBuilder append(AbstractStringBuilder asb) {
-        if (asb == null)
+        if (asb == null) {
             return appendNull();
+        }
         int len = asb.length();
         ensureCapacityInternal(count + len);
-        asb.getChars(0, len, value, count);
+        if (getCoder() != asb.getCoder()) {
+            inflate();
+        }
+        asb.getBytes(value, count, coder);
         count += len;
         return this;
     }
@@ -454,25 +515,35 @@
     // Documentation in subclasses because of synchro difference
     @Override
     public AbstractStringBuilder append(CharSequence s) {
-        if (s == null)
+        if (s == null) {
             return appendNull();
-        if (s instanceof String)
+        }
+        if (s instanceof String) {
             return this.append((String)s);
-        if (s instanceof AbstractStringBuilder)
+        }
+        if (s instanceof AbstractStringBuilder) {
             return this.append((AbstractStringBuilder)s);
-
+        }
         return this.append(s, 0, s.length());
     }
 
     private AbstractStringBuilder appendNull() {
-        int c = count;
-        ensureCapacityInternal(c + 4);
-        final char[] value = this.value;
-        value[c++] = 'n';
-        value[c++] = 'u';
-        value[c++] = 'l';
-        value[c++] = 'l';
-        count = c;
+        ensureCapacityInternal(count + 4);
+        int count = this.count;
+        byte[] val = this.value;
+        if (isLatin1()) {
+            val[count++] = 'n';
+            val[count++] = 'u';
+            val[count++] = 'l';
+            val[count++] = 'l';
+        } else {
+            checkOffset(count + 4, val.length >> 1);
+            StringUTF16.putChar(val, count++, 'n');
+            StringUTF16.putChar(val, count++, 'u');
+            StringUTF16.putChar(val, count++, 'l');
+            StringUTF16.putChar(val, count++, 'l');
+        }
+        this.count = count;
         return this;
     }
 
@@ -507,21 +578,13 @@
      */
     @Override
     public AbstractStringBuilder append(CharSequence s, int start, int end) {
-        if (s == null)
+        if (s == null) {
             s = "null";
-        if ((start < 0) || (start > end) || (end > s.length()))
-            throw new IndexOutOfBoundsException(
-                "start " + start + ", end " + end + ", s.length() "
-                + s.length());
+        }
+        checkRange(start, end, s.length());
         int len = end - start;
         ensureCapacityInternal(count + len);
-        if (s instanceof String) {
-            ((String)s).getChars(start, end, value, count);
-        } else {
-            for (int i = start, j = count; i < end; i++, j++)
-                value[j] = s.charAt(i);
-        }
-        count += len;
+        appendChars(s, start, end);
         return this;
     }
 
@@ -544,8 +607,7 @@
     public AbstractStringBuilder append(char[] str) {
         int len = str.length;
         ensureCapacityInternal(count + len);
-        System.arraycopy(str, 0, value, count, len);
-        count += len;
+        appendChars(str, 0, len);
         return this;
     }
 
@@ -572,10 +634,10 @@
      *         or {@code offset+len > str.length}
      */
     public AbstractStringBuilder append(char str[], int offset, int len) {
-        if (len > 0)                // let arraycopy report AIOOBE for len < 0
-            ensureCapacityInternal(count + len);
-        System.arraycopy(str, offset, value, count, len);
-        count += len;
+        int end = offset + len;
+        checkRange(offset, end, str.length);
+        ensureCapacityInternal(count + len);
+        appendChars(str, offset, end);
         return this;
     }
 
@@ -592,20 +654,39 @@
      * @return  a reference to this object.
      */
     public AbstractStringBuilder append(boolean b) {
-        if (b) {
-            ensureCapacityInternal(count + 4);
-            value[count++] = 't';
-            value[count++] = 'r';
-            value[count++] = 'u';
-            value[count++] = 'e';
+        ensureCapacityInternal(count + (b ? 4 : 5));
+        int count = this.count;
+        byte[] val = this.value;
+        if (isLatin1()) {
+            if (b) {
+                val[count++] = 't';
+                val[count++] = 'r';
+                val[count++] = 'u';
+                val[count++] = 'e';
+            } else {
+                val[count++] = 'f';
+                val[count++] = 'a';
+                val[count++] = 'l';
+                val[count++] = 's';
+                val[count++] = 'e';
+            }
         } else {
-            ensureCapacityInternal(count + 5);
-            value[count++] = 'f';
-            value[count++] = 'a';
-            value[count++] = 'l';
-            value[count++] = 's';
-            value[count++] = 'e';
+            if (b) {
+                checkOffset(count + 4, val.length >> 1);
+                StringUTF16.putChar(val, count++, 't');
+                StringUTF16.putChar(val, count++, 'r');
+                StringUTF16.putChar(val, count++, 'u');
+                StringUTF16.putChar(val, count++, 'e');
+            } else {
+                checkOffset(count + 5, val.length >> 1);
+                StringUTF16.putChar(val, count++, 'f');
+                StringUTF16.putChar(val, count++, 'a');
+                StringUTF16.putChar(val, count++, 'l');
+                StringUTF16.putChar(val, count++, 's');
+                StringUTF16.putChar(val, count++, 'e');
+            }
         }
+        this.count = count;
         return this;
     }
 
@@ -627,7 +708,14 @@
     @Override
     public AbstractStringBuilder append(char c) {
         ensureCapacityInternal(count + 1);
-        value[count++] = c;
+        if (isLatin1() && StringLatin1.canEncode(c)) {
+            value[count++] = (byte)c;
+        } else {
+            if (isLatin1()) {
+                inflate();
+            }
+            StringUTF16.putCharSB(value, count++, c);
+        }
         return this;
     }
 
@@ -652,7 +740,13 @@
                                      : Integer.stringSize(i);
         int spaceNeeded = count + appendedLength;
         ensureCapacityInternal(spaceNeeded);
-        Integer.getChars(i, spaceNeeded, value);
+        if (isLatin1()) {
+            Integer.getChars(i, spaceNeeded, value);
+        } else {
+            byte[] val = this.value;
+            checkOffset(spaceNeeded, val.length >> 1);
+            Integer.getCharsUTF16(i, spaceNeeded, val);
+        }
         count = spaceNeeded;
         return this;
     }
@@ -678,7 +772,13 @@
                                      : Long.stringSize(l);
         int spaceNeeded = count + appendedLength;
         ensureCapacityInternal(spaceNeeded);
-        Long.getChars(l, spaceNeeded, value);
+        if (isLatin1()) {
+            Long.getChars(l, spaceNeeded, value);
+        } else {
+            byte[] val = this.value;
+            checkOffset(spaceNeeded, val.length >> 1);
+            Long.getCharsUTF16(l, spaceNeeded, val);
+        }
         count = spaceNeeded;
         return this;
     }
@@ -732,15 +832,13 @@
      *             greater than {@code end}.
      */
     public AbstractStringBuilder delete(int start, int end) {
-        if (start < 0)
-            throw new StringIndexOutOfBoundsException(start);
-        if (end > count)
+        if (end > count) {
             end = count;
-        if (start > end)
-            throw new StringIndexOutOfBoundsException();
+        }
+        checkRangeSIOOBE(start, end, count);
         int len = end - start;
         if (len > 0) {
-            System.arraycopy(value, start+len, value, start, count-end);
+            shift(end, -len);
             count -= len;
         }
         return this;
@@ -766,20 +864,10 @@
      * {@code codePoint} isn't a valid Unicode code point
      */
     public AbstractStringBuilder appendCodePoint(int codePoint) {
-        final int count = this.count;
-
         if (Character.isBmpCodePoint(codePoint)) {
-            ensureCapacityInternal(count + 1);
-            value[count] = (char) codePoint;
-            this.count = count + 1;
-        } else if (Character.isValidCodePoint(codePoint)) {
-            ensureCapacityInternal(count + 2);
-            Character.toSurrogates(codePoint, value, count);
-            this.count = count + 2;
-        } else {
-            throw new IllegalArgumentException();
+            return append((char)codePoint);
         }
-        return this;
+        return append(Character.toChars(codePoint));
     }
 
     /**
@@ -800,9 +888,8 @@
      *              {@code length()}.
      */
     public AbstractStringBuilder deleteCharAt(int index) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        System.arraycopy(value, index+1, value, index, count-index-1);
+        checkIndex(index, count);
+        shift(index + 1, -1);
         count--;
         return this;
     }
@@ -827,22 +914,16 @@
      *             greater than {@code end}.
      */
     public AbstractStringBuilder replace(int start, int end, String str) {
-        if (start < 0)
-            throw new StringIndexOutOfBoundsException(start);
-        if (start > count)
-            throw new StringIndexOutOfBoundsException("start > length()");
-        if (start > end)
-            throw new StringIndexOutOfBoundsException("start > end");
-
-        if (end > count)
+        if (end > count) {
             end = count;
+        }
+        checkRangeSIOOBE(start, end, count);
         int len = str.length();
         int newCount = count + len - (end - start);
         ensureCapacityInternal(newCount);
-
-        System.arraycopy(value, end, value, start + len, count - end);
-        str.getChars(value, start);
+        shift(end, newCount - count);
         count = newCount;
+        putStringAt(start, str);
         return this;
     }
 
@@ -907,13 +988,16 @@
      *             greater than {@code end}.
      */
     public String substring(int start, int end) {
-        if (start < 0)
-            throw new StringIndexOutOfBoundsException(start);
-        if (end > count)
-            throw new StringIndexOutOfBoundsException(end);
-        if (start > end)
-            throw new StringIndexOutOfBoundsException(end - start);
-        return new String(value, start, end - start);
+        checkRangeSIOOBE(start, end, count);
+        if (isLatin1()) {
+            return StringLatin1.newString(value, start, end - start);
+        }
+        return StringUTF16.newStringSB(value, start, end - start);
+    }
+
+    private void shift(int offset, int n) {
+        System.arraycopy(value, offset << coder,
+                         value, (offset + n) << coder, (count - offset) << coder);
     }
 
     /**
@@ -940,16 +1024,12 @@
     public AbstractStringBuilder insert(int index, char[] str, int offset,
                                         int len)
     {
-        if ((index < 0) || (index > length()))
-            throw new StringIndexOutOfBoundsException(index);
-        if ((offset < 0) || (len < 0) || (offset > str.length - len))
-            throw new StringIndexOutOfBoundsException(
-                "offset " + offset + ", len " + len + ", str.length "
-                + str.length);
+        checkOffset(index, count);
+        checkRangeSIOOBE(offset, offset + len, str.length);
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, index, value, index + len, count - index);
-        System.arraycopy(str, offset, value, index, len);
+        shift(index, len);
         count += len;
+        putCharsAt(index, str, offset, offset + len);
         return this;
     }
 
@@ -1008,15 +1088,15 @@
      * @throws     StringIndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int offset, String str) {
-        if ((offset < 0) || (offset > length()))
-            throw new StringIndexOutOfBoundsException(offset);
-        if (str == null)
+        checkOffset(offset, count);
+        if (str == null) {
             str = "null";
+        }
         int len = str.length();
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, offset, value, offset + len, count - offset);
-        str.getChars(value, offset);
+        shift(offset, len);
         count += len;
+        putStringAt(offset, str);
         return this;
     }
 
@@ -1045,13 +1125,12 @@
      * @throws     StringIndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int offset, char[] str) {
-        if ((offset < 0) || (offset > length()))
-            throw new StringIndexOutOfBoundsException(offset);
+        checkOffset(offset, count);
         int len = str.length;
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, offset, value, offset + len, count - offset);
-        System.arraycopy(str, 0, value, offset, len);
+        shift(offset, len);
         count += len;
+        putCharsAt(offset, str, 0, len);
         return this;
     }
 
@@ -1077,10 +1156,12 @@
      * @throws     IndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int dstOffset, CharSequence s) {
-        if (s == null)
+        if (s == null) {
             s = "null";
-        if (s instanceof String)
+        }
+        if (s instanceof String) {
             return this.insert(dstOffset, (String)s);
+        }
         return this.insert(dstOffset, s, 0, s.length());
     }
 
@@ -1128,23 +1209,19 @@
      *              {@code start} is greater than {@code end} or
      *              {@code end} is greater than {@code s.length()}
      */
-     public AbstractStringBuilder insert(int dstOffset, CharSequence s,
-                                         int start, int end) {
-        if (s == null)
+    public AbstractStringBuilder insert(int dstOffset, CharSequence s,
+                                        int start, int end)
+    {
+        if (s == null) {
             s = "null";
-        if ((dstOffset < 0) || (dstOffset > this.length()))
-            throw new IndexOutOfBoundsException("dstOffset "+dstOffset);
-        if ((start < 0) || (end < 0) || (start > end) || (end > s.length()))
-            throw new IndexOutOfBoundsException(
-                "start " + start + ", end " + end + ", s.length() "
-                + s.length());
+        }
+        checkOffset(dstOffset, count);
+        checkRange(start, end, s.length());
         int len = end - start;
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, dstOffset, value, dstOffset + len,
-                         count - dstOffset);
-        for (int i=start; i<end; i++)
-            value[dstOffset++] = s.charAt(i);
+        shift(dstOffset, len);
         count += len;
+        putCharsAt(dstOffset, s, start, end);
         return this;
     }
 
@@ -1191,10 +1268,18 @@
      * @throws     IndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int offset, char c) {
+        checkOffset(offset, count);
         ensureCapacityInternal(count + 1);
-        System.arraycopy(value, offset, value, offset + 1, count - offset);
-        value[offset] = c;
+        shift(offset, 1);
         count += 1;
+        if (isLatin1() && StringLatin1.canEncode(c)) {
+            value[offset] = (byte)c;
+        } else {
+            if (isLatin1()) {
+                inflate();
+            }
+            StringUTF16.putCharSB(value, offset, c);
+        }
         return this;
     }
 
@@ -1326,7 +1411,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int indexOf(String str, int fromIndex) {
-        return String.indexOf(value, 0, count, str, fromIndex);
+        return String.indexOf(value, coder, count, str, fromIndex);
     }
 
     /**
@@ -1366,7 +1451,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int lastIndexOf(String str, int fromIndex) {
-        return String.lastIndexOf(value, 0, count, str, fromIndex);
+        return String.lastIndexOf(value, coder, count, str, fromIndex);
     }
 
     /**
@@ -1392,34 +1477,47 @@
      * @return  a reference to this object.
      */
     public AbstractStringBuilder reverse() {
-        boolean hasSurrogates = false;
+        byte[] val = this.value;
+        int count = this.count;
+        int coder = this.coder;
         int n = count - 1;
-        for (int j = (n-1) >> 1; j >= 0; j--) {
-            int k = n - j;
-            char cj = value[j];
-            char ck = value[k];
-            value[j] = ck;
-            value[k] = cj;
-            if (Character.isSurrogate(cj) ||
-                Character.isSurrogate(ck)) {
-                hasSurrogates = true;
+        if (COMPACT_STRINGS && coder == LATIN1) {
+            for (int j = (n-1) >> 1; j >= 0; j--) {
+                int k = n - j;
+                byte cj = val[j];
+                val[j] = val[k];
+                val[k] = cj;
             }
-        }
-        if (hasSurrogates) {
-            reverseAllValidSurrogatePairs();
+        } else {
+            checkOffset(count, val.length >> 1);
+            boolean hasSurrogates = false;
+            for (int j = (n-1) >> 1; j >= 0; j--) {
+                int k = n - j;
+                char cj = StringUTF16.getChar(val, j);
+                char ck = StringUTF16.getChar(val, k);
+                StringUTF16.putChar(val, j, ck);
+                StringUTF16.putChar(val, k, cj);
+                if (Character.isSurrogate(cj) ||
+                    Character.isSurrogate(ck)) {
+                    hasSurrogates = true;
+                }
+            }
+            if (hasSurrogates) {
+                reverseAllValidSurrogatePairs(val, count);
+            }
         }
         return this;
     }
 
     /** Outlined helper method for reverse() */
-    private void reverseAllValidSurrogatePairs() {
+    private void reverseAllValidSurrogatePairs(byte[] val, int count) {
         for (int i = 0; i < count - 1; i++) {
-            char c2 = value[i];
+            char c2 = StringUTF16.getChar(val, i);
             if (Character.isLowSurrogate(c2)) {
-                char c1 = value[i + 1];
+                char c1 = StringUTF16.getChar(val, i + 1);
                 if (Character.isHighSurrogate(c1)) {
-                    value[i++] = c1;
-                    value[i] = c2;
+                    StringUTF16.putChar(val, i++, c1);
+                    StringUTF16.putChar(val, i, c2);
                 }
             }
         }
@@ -1444,10 +1542,13 @@
      */
     @Override
     public IntStream chars() {
+        byte[] val = this.value; int count = this.count; byte coder = this.coder;
+        checkOffset(count, val.length >> coder);
         // Reuse String-based spliterator. This requires a supplier to
         // capture the value and count when the terminal operation is executed
         return StreamSupport.intStream(
-                () -> new String.IntCharArraySpliterator(value, 0, count, 0),
+                () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0)
+                                      : new StringUTF16.CharsSpliterator(val, 0, count, 0),
                 Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED,
                 false);
     }
@@ -1458,10 +1559,13 @@
      */
     @Override
     public IntStream codePoints() {
+        byte[] val = this.value; int count = this.count; byte coder = this.coder;
+        checkOffset(count, val.length >> coder);
         // Reuse String-based spliterator. This requires a supplier to
         // capture the value and count when the terminal operation is executed
         return StreamSupport.intStream(
-                () -> new String.CodePointsSpliterator(value, 0, count, 0),
+                () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0)
+                                      : new StringUTF16.CodePointsSpliterator(val, 0, count, 0),
                 Spliterator.ORDERED,
                 false);
     }
@@ -1469,8 +1573,147 @@
     /**
      * Needed by {@code String} for the contentEquals method.
      */
-    final char[] getValue() {
+    final byte[] getValue() {
         return value;
     }
 
+    /*
+     * Invoker guarantees it is in UTF16 (inflate itself for asb), if two
+     * coders are different and the dstBegin has enough space
+     *
+     * @param dstBegin  the char index, not offset of byte[]
+     * @param coder     the coder of dst[]
+     */
+    protected void getBytes(byte dst[], int dstBegin, byte coder) {
+        if (this.coder == coder) {
+            System.arraycopy(value, 0, dst, dstBegin << coder, count << coder);
+        } else {        // this.coder == LATIN && coder == UTF16
+            StringLatin1.inflateSB(value, dst, dstBegin, count);
+        }
+    }
+
+    /* for readObject() */
+    protected void initBytes(char[] value, int off, int len) {
+        if (String.COMPACT_STRINGS) {
+            this.value = StringUTF16.compress(value, off, len);
+            if (this.value != null) {
+                this.coder = LATIN1;
+                return;
+            }
+        }
+        this.coder = UTF16;
+        this.value = StringUTF16.toBytes(value, off, len);
+    }
+
+    final byte getCoder() {
+        return COMPACT_STRINGS ? coder : UTF16;
+    }
+
+    final boolean isLatin1() {
+        return COMPACT_STRINGS && coder == LATIN1;
+    }
+
+    private final void putCharsAt(int index, char[] s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = index; i < end; i++) {
+                char c = s[i];
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, index, s, off, end);
+        }
+    }
+
+    private final void putCharsAt(int index, CharSequence s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = index; i < end; i++) {
+                char c = s.charAt(i);
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, index, s, off, end);
+        }
+    }
+
+    private final void putStringAt(int index, String str) {
+        if (getCoder() != str.coder()) {
+            inflate();
+        }
+        byte[] val = this.value;
+        byte coder = this.coder;
+        checkOffset(index + str.length(), val.length >> coder);
+        str.getBytes(val, index, coder);
+    }
+
+    private final void appendChars(char[] s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = count; i < end; i++) {
+                char c = s[i];
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    count = j;
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    count += end - i;
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, count, s, off, end);
+        }
+        count += end - off;
+    }
+
+    private final void appendChars(CharSequence s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = count; i < end; i++) {
+                char c = s.charAt(i);
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    count = j;
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    count += end - i;
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, count, s, off, end);
+        }
+        count += end - off;
+    }
+
+    /* IndexOutOfBoundsException, if out of bounds */
+    private static void checkRange(int start, int end, int len) {
+        if (start < 0 || start > end || end > len) {
+            throw new IndexOutOfBoundsException(
+                "start " + start + ", end " + end + ", length " + len);
+        }
+    }
+
+    /* StringIndexOutOfBoundsException, if out of bounds */
+    private static void checkRangeSIOOBE(int start, int end, int len) {
+        if (start < 0 || start > end || end > len) {
+            throw new StringIndexOutOfBoundsException(
+                "start " + start + ", end " + end + ", length " + len);
+        }
+    }
 }
--- a/src/java.base/share/classes/java/lang/Integer.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/lang/Integer.java	Mon Nov 09 20:37:05 2015 -0800
@@ -29,6 +29,10 @@
 import java.util.Objects;
 import jdk.internal.HotSpotIntrinsicCandidate;
 
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+
 /**
  * The {@code Integer} class wraps a value of the primitive type
  * {@code int} in an object. An object of type {@code Integer}
@@ -138,25 +142,47 @@
             return toString(i);
         }
 
-        char buf[] = new char[33];
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[33];
+            boolean negative = (i < 0);
+            int charPos = 32;
+
+            if (!negative) {
+                i = -i;
+            }
+
+            while (i <= -radix) {
+                buf[charPos--] = (byte)digits[-(i % radix)];
+                i = i / radix;
+            }
+            buf[charPos] = (byte)digits[-i];
+
+            if (negative) {
+                buf[--charPos] = '-';
+            }
+
+            return StringLatin1.newString(buf, charPos, (33 - charPos));
+        }
+        return toStringUTF16(i, radix);
+    }
+
+    private static String toStringUTF16(int i, int radix) {
+        byte[] buf = new byte[33 * 2];
         boolean negative = (i < 0);
         int charPos = 32;
-
         if (!negative) {
             i = -i;
         }
-
         while (i <= -radix) {
-            buf[charPos--] = digits[-(i % radix)];
+            StringUTF16.putChar(buf, charPos--, digits[-(i % radix)]);
             i = i / radix;
         }
-        buf[charPos] = digits[-i];
+        StringUTF16.putChar(buf, charPos, digits[-i]);
 
         if (negative) {
-            buf[--charPos] = '-';
+            StringUTF16.putChar(buf, --charPos, '-');
         }
-
-        return new String(buf, charPos, (33 - charPos));
+        return StringUTF16.newString(buf, charPos, (33 - charPos));
     }
 
     /**
@@ -312,12 +338,16 @@
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
         int chars = Math.max(((mag + (shift - 1)) / shift), 1);
-        char[] buf = new char[chars];
 
-        formatUnsignedInt(val, shift, buf, 0, chars);
-
-        // Use special constructor which takes over "buf".
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[chars];
+            formatUnsignedInt(val, shift, buf, 0, chars);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[chars * 2];
+            formatUnsignedIntUTF16(val, shift, buf, 0, chars);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -331,7 +361,7 @@
      * @param offset the offset in the destination buffer to start at
      * @param len the number of characters to write
      */
-     static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
+    static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         // assert offset >= 0 && offset < buf.length : "illegal offset";
         // assert len > 0 && (offset + len) <= buf.length : "illegal length";
@@ -344,6 +374,28 @@
         } while (charPos > offset);
     }
 
+    /** byte[]/LATIN1 version    */
+    static void formatUnsignedInt(int val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            buf[--charPos] = (byte)Integer.digits[val & mask];
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
+    /** byte[]/UTF16 version    */
+    static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            StringUTF16.putChar(buf, --charPos, Integer.digits[val & mask]);
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
     static final char [] DigitTens = {
         '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
         '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
@@ -401,9 +453,15 @@
         if (i == Integer.MIN_VALUE)
             return "-2147483648";
         int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
-        char[] buf = new char[size];
-        getChars(i, size, buf);
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[size];
+            getChars(i, size, buf);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[size * 2];
+            getCharsUTF16(i, size, buf);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -433,7 +491,7 @@
      *
      * Will fail if i == Integer.MIN_VALUE
      */
-    static void getChars(int i, int index, char[] buf) {
+    static void getChars(int i, int index, byte[] buf) {
         int q, r;
         int charPos = index;
         char sign = 0;
@@ -449,8 +507,8 @@
         // really: r = i - (q * 100);
             r = i - ((q << 6) + (q << 5) + (q << 2));
             i = q;
-            buf [--charPos] = DigitOnes[r];
-            buf [--charPos] = DigitTens[r];
+            buf [--charPos] = (byte)DigitOnes[r];
+            buf [--charPos] = (byte)DigitTens[r];
         }
 
         // Fall thru to fast mode for smaller numbers
@@ -458,12 +516,46 @@
         for (;;) {
             q = (i * 52429) >>> (16+3);
             r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
-            buf [--charPos] = digits [r];
+            buf [--charPos] = (byte)digits [r];
             i = q;
             if (i == 0) break;
         }
         if (sign != 0) {
-            buf [--charPos] = sign;
+            buf [--charPos] = (byte)sign;
+        }
+    }
+
+    static void getCharsUTF16(int i, int index, byte[] buf) {
+        int q, r;
+        int charPos = index;
+        char sign = 0;
+
+        if (i < 0) {
+            sign = '-';
+            i = -i;
+        }
+
+        // Generate two digits per iteration
+        while (i >= 65536) {
+            q = i / 100;
+        // really: r = i - (q * 100);
+            r = i - ((q << 6) + (q << 5) + (q << 2));
+            i = q;
+            StringUTF16.putChar(buf, --charPos, DigitOnes[r]);
+            StringUTF16.putChar(buf, --charPos, DigitTens[r]);
+        }
+
+        // Fall thru to fast mode for smaller numbers
+        // assert(i <= 65536, i);
+        for (;;) {
+            q = (i * 52429) >>> (16+3);
+            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
+            StringUTF16.putChar(buf, --charPos, Integer.digits[r]);
+            i = q;
+            if (i == 0) break;
+        }
+        if (sign != 0) {
+            StringUTF16.putChar(buf, --charPos, sign);
         }
     }
 
--- a/src/java.base/share/classes/java/lang/Long.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/lang/Long.java	Mon Nov 09 20:37:05 2015 -0800
@@ -30,6 +30,9 @@
 import java.util.Objects;
 import jdk.internal.HotSpotIntrinsicCandidate;
 
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
 
 /**
  * The {@code Long} class wraps a value of the primitive type {@code
@@ -124,25 +127,46 @@
             radix = 10;
         if (radix == 10)
             return toString(i);
-        char[] buf = new char[65];
+
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[65];
+            int charPos = 64;
+            boolean negative = (i < 0);
+
+            if (!negative) {
+                i = -i;
+            }
+
+            while (i <= -radix) {
+                buf[charPos--] = (byte)Integer.digits[(int)(-(i % radix))];
+                i = i / radix;
+            }
+            buf[charPos] = (byte)Integer.digits[(int)(-i)];
+
+            if (negative) {
+                buf[--charPos] = '-';
+            }
+            return StringLatin1.newString(buf, charPos, (65 - charPos));
+        }
+        return toStringUTF16(i, radix);
+    }
+
+    private static String toStringUTF16(long i, int radix) {
+        byte[] buf = new byte[65 * 2];
         int charPos = 64;
         boolean negative = (i < 0);
-
         if (!negative) {
             i = -i;
         }
-
         while (i <= -radix) {
-            buf[charPos--] = Integer.digits[(int)(-(i % radix))];
+            StringUTF16.putChar(buf, charPos--, Integer.digits[(int)(-(i % radix))]);
             i = i / radix;
         }
-        buf[charPos] = Integer.digits[(int)(-i)];
-
+        StringUTF16.putChar(buf, charPos, Integer.digits[(int)(-i)]);
         if (negative) {
-            buf[--charPos] = '-';
+            StringUTF16.putChar(buf, --charPos, '-');
         }
-
-        return new String(buf, charPos, (65 - charPos));
+        return StringUTF16.newString(buf, charPos, (65 - charPos));
     }
 
     /**
@@ -355,10 +379,16 @@
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
         int chars = Math.max(((mag + (shift - 1)) / shift), 1);
-        char[] buf = new char[chars];
 
-        formatUnsignedLong(val, shift, buf, 0, chars);
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[chars];
+            formatUnsignedLong0(val, shift, buf, 0, chars);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[chars * 2];
+            formatUnsignedLong0UTF16(val, shift, buf, 0, chars);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -385,6 +415,28 @@
         } while (charPos > offset);
     }
 
+    /** byte[]/LATIN1 version    */
+    static void formatUnsignedLong0(long val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            buf[--charPos] = (byte)Integer.digits[((int) val) & mask];
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
+    /** byte[]/UTF16 version    */
+    static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            StringUTF16.putChar(buf, --charPos, Integer.digits[((int) val) & mask]);
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
     /**
      * Returns a {@code String} object representing the specified
      * {@code long}.  The argument is converted to signed decimal
@@ -399,9 +451,15 @@
         if (i == Long.MIN_VALUE)
             return "-9223372036854775808";
         int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
-        char[] buf = new char[size];
-        getChars(i, size, buf);
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[size];
+            getChars(i, size, buf);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[size * 2];
+            getCharsUTF16(i, size, buf);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -431,7 +489,7 @@
      *
      * Will fail if i == Long.MIN_VALUE
      */
-    static void getChars(long i, int index, char[] buf) {
+    static void getChars(long i, int index, byte[] buf) {
         long q;
         int r;
         int charPos = index;
@@ -448,8 +506,8 @@
             // really: r = i - (q * 100);
             r = (int)(i - ((q << 6) + (q << 5) + (q << 2)));
             i = q;
-            buf[--charPos] = Integer.DigitOnes[r];
-            buf[--charPos] = Integer.DigitTens[r];
+            buf[--charPos] = (byte)Integer.DigitOnes[r];
+            buf[--charPos] = (byte)Integer.DigitTens[r];
         }
 
         // Get 2 digits/iteration using ints
@@ -460,8 +518,8 @@
             // really: r = i2 - (q * 100);
             r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
             i2 = q2;
-            buf[--charPos] = Integer.DigitOnes[r];
-            buf[--charPos] = Integer.DigitTens[r];
+            buf[--charPos] = (byte)Integer.DigitOnes[r];
+            buf[--charPos] = (byte)Integer.DigitTens[r];
         }
 
         // Fall thru to fast mode for smaller numbers
@@ -469,12 +527,59 @@
         for (;;) {
             q2 = (i2 * 52429) >>> (16+3);
             r = i2 - ((q2 << 3) + (q2 << 1));  // r = i2-(q2*10) ...
-            buf[--charPos] = Integer.digits[r];
+            buf[--charPos] = (byte)Integer.digits[r];
             i2 = q2;
             if (i2 == 0) break;
         }
         if (sign != 0) {
-            buf[--charPos] = sign;
+            buf[--charPos] = (byte)sign;
+        }
+    }
+
+    static void getCharsUTF16(long i, int index, byte[] buf) {
+        long q;
+        int r;
+        int charPos = index;
+        char sign = 0;
+
+        if (i < 0) {
+            sign = '-';
+            i = -i;
+        }
+
+        // Get 2 digits/iteration using longs until quotient fits into an int
+        while (i > Integer.MAX_VALUE) {
+            q = i / 100;
+            // really: r = i - (q * 100);
+            r = (int)(i - ((q << 6) + (q << 5) + (q << 2)));
+            i = q;
+            StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]);
+            StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]);
+        }
+
+        // Get 2 digits/iteration using ints
+        int q2;
+        int i2 = (int)i;
+        while (i2 >= 65536) {
+            q2 = i2 / 100;
+            // really: r = i2 - (q * 100);
+            r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
+            i2 = q2;
+            StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]);
+            StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]);
+        }
+
+        // Fall thru to fast mode for smaller numbers
+        // assert(i2 <= 65536, i2);
+        for (;;) {
+            q2 = (i2 * 52429) >>> (16+3);
+            r = i2 - ((q2 << 3) + (q2 << 1));  // r = i2-(q2*10) ...
+            StringUTF16.putChar(buf, --charPos, Integer.digits[r]);
+            i2 = q2;
+            if (i2 == 0) break;
+        }
+        if (sign != 0) {
+            StringUTF16.putChar(buf, --charPos, sign);
         }
     }
 
--- a/src/java.base/share/classes/java/lang/String.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/lang/String.java	Mon Nov 09 20:37:05 2015 -0800
@@ -36,7 +36,6 @@
 import java.util.Objects;
 import java.util.Spliterator;
 import java.util.StringJoiner;
-import java.util.function.IntConsumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -119,8 +118,19 @@
 
 public final class String
     implements java.io.Serializable, Comparable<String>, CharSequence {
+
     /** The value is used for character storage. */
-    private final char value[];
+    private final byte[] value;
+
+    /**
+     * The identifier of the encoding used to encode the bytes in
+     * {@code value}. The supported values in this implementation are
+     *
+     * LATIN1
+     * UTF16
+     *
+     */
+    private final byte coder;
 
     /** Cache the hash code for the string */
     private int hash; // Default to 0
@@ -129,6 +139,49 @@
     private static final long serialVersionUID = -6849794470754667710L;
 
     /**
+     * If String compaction is disabled, the bytes in {@code value} are
+     * always encoded in UTF16.
+     *
+     * For methods with several possible implementation paths, when String
+     * compaction is disabled, only one code path is taken.
+     *
+     * The instance field value is generally opaque to optimizing JIT
+     * compilers. Therefore, in performance-sensitive place, an explicit
+     * check of the static boolean {@code COMPACT_STRINGS} is done first
+     * before checking the {@code coder} field since the static boolean
+     * {@code COMPACT_STRINGS} would be constant folded away by an
+     * optimizing JIT compiler. The idioms for these cases are as follows.
+     *
+     * For code such as:
+     *
+     *    if (coder == LATIN1) { ... }
+     *
+     * can be written more optimally as
+     *
+     *    if (coder() == LATIN1) { ... }
+     *
+     * or:
+     *
+     *    if (COMPACT_STRINGS && coder == LATIN1) { ... }
+     *
+     * An optimizing JIT compiler can fold the above conditional as:
+     *
+     *    COMPACT_STRINGS == true  => if (coder == LATIN1) { ... }
+     *    COMPACT_STRINGS == false => if (false)           { ... }
+     *
+     * @implNote
+     * The actual value for this field is injected by JVM. The static
+     * initialization block is used to set the value here to communicate
+     * that this static final field is not statically foldable, and to
+     * avoid any possible circular dependency during vm initialization.
+     */
+    static final boolean COMPACT_STRINGS;
+
+    static {
+        COMPACT_STRINGS = true;
+    }
+
+    /**
      * Class String is special cased within the Serialization Stream Protocol.
      *
      * A String instance is written into an ObjectOutputStream according to
@@ -145,6 +198,7 @@
      */
     public String() {
         this.value = "".value;
+        this.coder = "".coder;
     }
 
     /**
@@ -160,6 +214,7 @@
     @HotSpotIntrinsicCandidate
     public String(String original) {
         this.value = original.value;
+        this.coder = original.coder;
         this.hash = original.hash;
     }
 
@@ -173,7 +228,7 @@
      *         The initial value of the string
      */
     public String(char value[]) {
-        this.value = Arrays.copyOf(value, value.length);
+        this(value, 0, value.length, null);
     }
 
     /**
@@ -198,23 +253,12 @@
      *          {@code offset} is greater than {@code value.length - count}
      */
     public String(char value[], int offset, int count) {
-        if (offset < 0) {
-            throw new StringIndexOutOfBoundsException(offset);
-        }
-        if (count <= 0) {
-            if (count < 0) {
-                throw new StringIndexOutOfBoundsException(count);
-            }
-            if (offset <= value.length) {
-                this.value = "".value;
-                return;
-            }
-        }
-        // Note: offset or count might be near -1>>>1.
-        if (offset > value.length - count) {
-            throw new StringIndexOutOfBoundsException(offset + count);
-        }
-        this.value = Arrays.copyOfRange(value, offset, offset + count);
+        this(value, offset, count, rangeCheck(value, offset, count));
+    }
+
+    private static Void rangeCheck(char[] value, int offset, int count) {
+        checkBoundsOffCount(offset, count, value.length);
+        return null;
     }
 
     /**
@@ -246,48 +290,22 @@
      * @since  1.5
      */
     public String(int[] codePoints, int offset, int count) {
-        if (offset < 0) {
-            throw new StringIndexOutOfBoundsException(offset);
+        checkBoundsOffCount(offset, count, codePoints.length);
+        if (count == 0) {
+            this.value = "".value;
+            this.coder = "".coder;
+            return;
         }
-        if (count <= 0) {
-            if (count < 0) {
-                throw new StringIndexOutOfBoundsException(count);
-            }
-            if (offset <= codePoints.length) {
-                this.value = "".value;
+        if (COMPACT_STRINGS) {
+            byte[] val = StringLatin1.toBytes(codePoints, offset, count);
+            if (val != null) {
+                this.coder = LATIN1;
+                this.value = val;
                 return;
             }
         }
-        // Note: offset or count might be near -1>>>1.
-        if (offset > codePoints.length - count) {
-            throw new StringIndexOutOfBoundsException(offset + count);
-        }
-
-        final int end = offset + count;
-
-        // Pass 1: Compute precise size of char[]
-        int n = count;
-        for (int i = offset; i < end; i++) {
-            int c = codePoints[i];
-            if (Character.isBmpCodePoint(c))
-                continue;
-            else if (Character.isValidCodePoint(c))
-                n++;
-            else throw new IllegalArgumentException(Integer.toString(c));
-        }
-
-        // Pass 2: Allocate and fill in char[]
-        final char[] v = new char[n];
-
-        for (int i = offset, j = 0; i < end; i++, j++) {
-            int c = codePoints[i];
-            if (Character.isBmpCodePoint(c))
-                v[j] = (char)c;
-            else
-                Character.toSurrogates(c, v, j++);
-        }
-
-        this.value = v;
+        this.coder = UTF16;
+        this.value = StringUTF16.toBytes(codePoints, offset, count);
     }
 
     /**
@@ -332,20 +350,24 @@
      */
     @Deprecated
     public String(byte ascii[], int hibyte, int offset, int count) {
-        checkBounds(ascii, offset, count);
-        char[] value = new char[count];
-
-        if (hibyte == 0) {
-            for (int i = count; i-- > 0;) {
-                value[i] = (char)(ascii[i + offset] & 0xff);
-            }
+        checkBoundsOffCount(offset, count, ascii.length);
+        if (count == 0) {
+            this.value = "".value;
+            this.coder = "".coder;
+            return;
+        }
+        if (COMPACT_STRINGS && (byte)hibyte == 0) {
+            this.value = Arrays.copyOfRange(ascii, offset, offset + count);
+            this.coder = LATIN1;
         } else {
             hibyte <<= 8;
-            for (int i = count; i-- > 0;) {
-                value[i] = (char)(hibyte | (ascii[i + offset] & 0xff));
+            byte[] val = StringUTF16.newBytesFor(count);
+            for (int i = 0; i < count; i++) {
+                StringUTF16.putChar(val, i, hibyte | (ascii[offset++] & 0xff));
             }
+            this.value = val;
+            this.coder = UTF16;
         }
-        this.value = value;
     }
 
     /**
@@ -383,19 +405,6 @@
         this(ascii, hibyte, 0, ascii.length);
     }
 
-    /* Common private utility method used to bounds check the byte array
-     * and requested offset & length values used by the String(byte[],..)
-     * constructors.
-     */
-    private static void checkBounds(byte[] bytes, int offset, int length) {
-        if (length < 0)
-            throw new StringIndexOutOfBoundsException(length);
-        if (offset < 0)
-            throw new StringIndexOutOfBoundsException(offset);
-        if (offset > bytes.length - length)
-            throw new StringIndexOutOfBoundsException(offset + length);
-    }
-
     /**
      * Constructs a new {@code String} by decoding the specified subarray of
      * bytes using the specified charset.  The length of the new {@code String}
@@ -433,8 +442,11 @@
             throws UnsupportedEncodingException {
         if (charsetName == null)
             throw new NullPointerException("charsetName");
-        checkBounds(bytes, offset, length);
-        this.value = StringCoding.decode(charsetName, bytes, offset, length);
+        checkBoundsOffCount(offset, length, bytes.length);
+        StringCoding.Result ret =
+            StringCoding.decode(charsetName, bytes, offset, length);
+        this.value = ret.value;
+        this.coder = ret.coder;
     }
 
     /**
@@ -470,8 +482,11 @@
     public String(byte bytes[], int offset, int length, Charset charset) {
         if (charset == null)
             throw new NullPointerException("charset");
-        checkBounds(bytes, offset, length);
-        this.value = StringCoding.decode(charset, bytes, offset, length);
+        checkBoundsOffCount(offset, length, bytes.length);
+        StringCoding.Result ret =
+            StringCoding.decode(charset, bytes, offset, length);
+        this.value = ret.value;
+        this.coder = ret.coder;
     }
 
     /**
@@ -553,8 +568,10 @@
      * @since  1.1
      */
     public String(byte bytes[], int offset, int length) {
-        checkBounds(bytes, offset, length);
-        this.value = StringCoding.decode(bytes, offset, length);
+        checkBoundsOffCount(offset, length, bytes.length);
+        StringCoding.Result ret = StringCoding.decode(bytes, offset, length);
+        this.value = ret.value;
+        this.coder = ret.coder;
     }
 
     /**
@@ -587,9 +604,7 @@
      *         A {@code StringBuffer}
      */
     public String(StringBuffer buffer) {
-        synchronized(buffer) {
-            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
-        }
+        this(buffer.toString());
     }
 
     /**
@@ -608,18 +623,20 @@
      * @since  1.5
      */
     public String(StringBuilder builder) {
-        this.value = Arrays.copyOf(builder.getValue(), builder.length());
+        this(builder, null);
     }
 
-    /*
+   /*
     * Package private constructor which shares value array for speed.
     * this constructor is always expected to be called with share==true.
     * a separate constructor is needed because we already have a public
     * String(char[]) constructor that makes a copy of the given char[].
     */
-    String(char[] value, boolean share) {
+    // TBD: this is kept for package internal use (Thread/System),
+    // should be removed if they all have a byte[] version
+    String(char[] val, boolean share) {
         // assert share : "unshared not supported";
-        this.value = value;
+        this(val, 0, val.length, null);
     }
 
     /**
@@ -631,7 +648,7 @@
      *          object.
      */
     public int length() {
-        return value.length;
+        return value.length >> coder();
     }
 
     /**
@@ -665,10 +682,11 @@
      *             string.
      */
     public char charAt(int index) {
-        if ((index < 0) || (index >= value.length)) {
-            throw new StringIndexOutOfBoundsException(index);
+        if (isLatin1()) {
+            return StringLatin1.charAt(value, index);
+        } else {
+            return StringUTF16.charAt(value, index);
         }
-        return value[index];
     }
 
     /**
@@ -694,10 +712,13 @@
      * @since      1.5
      */
     public int codePointAt(int index) {
-        if ((index < 0) || (index >= value.length)) {
-            throw new StringIndexOutOfBoundsException(index);
+        if (isLatin1()) {
+            checkIndex(index, value.length);
+            return value[index] & 0xff;
         }
-        return Character.codePointAtImpl(value, index, value.length);
+        int length = value.length >> 1;
+        checkIndex(index, length);
+        return StringUTF16.codePointAt(value, index, length);
     }
 
     /**
@@ -724,10 +745,13 @@
      */
     public int codePointBefore(int index) {
         int i = index - 1;
-        if ((i < 0) || (i >= value.length)) {
+        if (i < 0 || i >= length()) {
             throw new StringIndexOutOfBoundsException(index);
         }
-        return Character.codePointBeforeImpl(value, index, 0);
+        if (isLatin1()) {
+            return (value[i] & 0xff);
+        }
+        return StringUTF16.codePointBefore(value, index);
     }
 
     /**
@@ -752,10 +776,14 @@
      * @since  1.5
      */
     public int codePointCount(int beginIndex, int endIndex) {
-        if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
+        if (beginIndex < 0 || beginIndex > endIndex ||
+            endIndex > length()) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
+        if (isLatin1()) {
+            return endIndex - beginIndex;
+        }
+        return StringUTF16.codePointCount(value, beginIndex, endIndex);
     }
 
     /**
@@ -779,19 +807,10 @@
      * @since 1.5
      */
     public int offsetByCodePoints(int index, int codePointOffset) {
-        if (index < 0 || index > value.length) {
+        if (index < 0 || index > length()) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.offsetByCodePointsImpl(value, 0, value.length,
-                index, codePointOffset);
-    }
-
-    /**
-     * Copy characters from this string into dst starting at dstBegin.
-     * This method doesn't perform any range checking.
-     */
-    void getChars(char dst[], int dstBegin) {
-        System.arraycopy(value, 0, dst, dstBegin, value.length);
+        return Character.offsetByCodePoints(this, index, codePointOffset);
     }
 
     /**
@@ -825,16 +844,13 @@
      *                {@code dst.length}</ul>
      */
     public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
-        if (srcBegin < 0) {
-            throw new StringIndexOutOfBoundsException(srcBegin);
+        checkBoundsBeginEnd(srcBegin, srcEnd, length());
+        checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);
+        if (isLatin1()) {
+            StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin);
+        } else {
+            StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin);
         }
-        if (srcEnd > value.length) {
-            throw new StringIndexOutOfBoundsException(srcEnd);
-        }
-        if (srcBegin > srcEnd) {
-            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
-        }
-        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
     }
 
     /**
@@ -882,24 +898,13 @@
      */
     @Deprecated
     public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
-        if (srcBegin < 0) {
-            throw new StringIndexOutOfBoundsException(srcBegin);
-        }
-        if (srcEnd > value.length) {
-            throw new StringIndexOutOfBoundsException(srcEnd);
-        }
-        if (srcBegin > srcEnd) {
-            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
-        }
+        checkBoundsBeginEnd(srcBegin, srcEnd, length());
         Objects.requireNonNull(dst);
-
-        int j = dstBegin;
-        int n = srcEnd;
-        int i = srcBegin;
-        char[] val = value;   /* avoid getfield opcode */
-
-        while (i < n) {
-            dst[j++] = (byte)val[i++];
+        checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);
+        if (isLatin1()) {
+            StringLatin1.getBytes(value, srcBegin, srcEnd, dst, dstBegin);
+        } else {
+            StringUTF16.getBytes(value, srcBegin, srcEnd, dst, dstBegin);
         }
     }
 
@@ -926,7 +931,7 @@
     public byte[] getBytes(String charsetName)
             throws UnsupportedEncodingException {
         if (charsetName == null) throw new NullPointerException();
-        return StringCoding.encode(charsetName, value, 0, value.length);
+        return StringCoding.encode(charsetName, coder(), value);
     }
 
     /**
@@ -949,8 +954,8 @@
      */
     public byte[] getBytes(Charset charset) {
         if (charset == null) throw new NullPointerException();
-        return StringCoding.encode(charset, value, 0, value.length);
-    }
+        return StringCoding.encode(charset, coder(), value);
+     }
 
     /**
      * Encodes this {@code String} into a sequence of bytes using the
@@ -966,7 +971,7 @@
      * @since      1.1
      */
     public byte[] getBytes() {
-        return StringCoding.encode(value, 0, value.length);
+        return StringCoding.encode(coder(), value);
     }
 
     /**
@@ -987,23 +992,15 @@
      * @see  #compareTo(String)
      * @see  #equalsIgnoreCase(String)
      */
-    @HotSpotIntrinsicCandidate
     public boolean equals(Object anObject) {
         if (this == anObject) {
             return true;
         }
         if (anObject instanceof String) {
-            char[] v1 = value;
-            char[] v2 = ((String)anObject).value;
-            int n = v1.length;
-            if (n == v2.length) {
-                int i = 0;
-                while (n-- != 0) {
-                    if (v1[i] != v2[i])
-                        return false;
-                    i++;
-                }
-                return true;
+            String aString = (String)anObject;
+            if (coder() == aString.coder()) {
+                return isLatin1() ? StringLatin1.equals(value, aString.value)
+                                  : StringUTF16.equals(value, aString.value);
             }
         }
         return false;
@@ -1032,16 +1029,28 @@
     }
 
     private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
-        char[] v1 = value;
-        char[] v2 = sb.getValue();
-        int n = v1.length;
-        if (n != sb.length()) {
+        int len = length();
+        if (len != sb.length()) {
             return false;
         }
-        for (int i = 0; i < n; i++) {
-            if (v1[i] != v2[i]) {
+        byte v1[] = value;
+        byte v2[] = sb.getValue();
+        if (coder() == sb.getCoder()) {
+            int n = v1.length;
+            for (int i = 0; i < n; i++) {
+                if (v1[i] != v2[i]) {
+                    return false;
+                }
+            }
+        } else {
+            if (!isLatin1()) {  // utf16 str and latin1 abs can never be "equal"
                 return false;
             }
+            for (int i = 0; i < len; i++) {
+                if ((char)(v1[i] & 0xff) != StringUTF16.getChar(v2, i)) {
+                    return false;
+                }
+            }
         }
         return true;
     }
@@ -1081,14 +1090,22 @@
             return equals(cs);
         }
         // Argument is a generic CharSequence
-        char[] v1 = value;
-        int n = v1.length;
-        if (n != cs.length()) {
+        int n = cs.length();
+        if (n != length()) {
             return false;
         }
-        for (int i = 0; i < n; i++) {
-            if (v1[i] != cs.charAt(i)) {
-                return false;
+        byte[] val = this.value;
+        if (isLatin1()) {
+            for (int i = 0; i < n; i++) {
+                if ((val[i] & 0xff) != cs.charAt(i)) {
+                    return false;
+                }
+            }
+        } else {
+            for (int i = 0; i < n; i++) {
+                if (StringUTF16.getChar(val, i) != cs.charAt(i)) {
+                    return false;
+                }
             }
         }
         return true;
@@ -1125,8 +1142,8 @@
     public boolean equalsIgnoreCase(String anotherString) {
         return (this == anotherString) ? true
                 : (anotherString != null)
-                && (anotherString.value.length == value.length)
-                && regionMatches(true, 0, anotherString, 0, value.length);
+                && (anotherString.length() == length())
+                && regionMatches(true, 0, anotherString, 0, length());
     }
 
     /**
@@ -1173,23 +1190,16 @@
      *          value greater than {@code 0} if this string is
      *          lexicographically greater than the string argument.
      */
-    @HotSpotIntrinsicCandidate
     public int compareTo(String anotherString) {
-        char[] v1 = value;
-        char[] v2 = anotherString.value;
-        int len1 = v1.length;
-        int len2 = v2.length;
-        int lim = Math.min(len1, len2);
-
-        for (int k = 0; k < lim; k++) {
-            char c1 = v1[k];
-            char c2 = v2[k];
-            if (c1 != c2) {
-                return c1 - c2;
-            }
+        byte v1[] = value;
+        byte v2[] = anotherString.value;
+        if (coder() == anotherString.coder()) {
+            return isLatin1() ? StringLatin1.compareTo(v1, v2)
+                              : StringUTF16.compareTo(v1, v2);
         }
-        return len1 - len2;
-    }
+        return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
+                          : StringUTF16.compareToLatin1(v1, v2);
+     }
 
     /**
      * A Comparator that orders {@code String} objects as by
@@ -1210,12 +1220,18 @@
         private static final long serialVersionUID = 8575799808933029326L;
 
         public int compare(String s1, String s2) {
+            byte v1[] = s1.value;
+            byte v2[] = s2.value;
             int n1 = s1.length();
             int n2 = s2.length();
+            boolean s1IsLatin1 = s1.isLatin1();
+            boolean s2IsLatin1 = s2.isLatin1();
             int min = Math.min(n1, n2);
             for (int i = 0; i < min; i++) {
-                char c1 = s1.charAt(i);
-                char c2 = s2.charAt(i);
+                char c1 = s1IsLatin1 ? StringLatin1.getChar(v1, i)
+                                     : StringUTF16.getChar(v1, i);
+                char c2 = s2IsLatin1 ? StringLatin1.getChar(v2, i)
+                                     : StringUTF16.getChar(v2, i);
                 if (c1 != c2) {
                     c1 = Character.toUpperCase(c1);
                     c2 = Character.toUpperCase(c2);
@@ -1294,21 +1310,41 @@
      *          exactly matches the specified subregion of the string argument;
      *          {@code false} otherwise.
      */
-    public boolean regionMatches(int toffset, String other, int ooffset,
-            int len) {
-        char[] ta = value;
-        int to = toffset;
-        char[] pa = other.value;
-        int po = ooffset;
+    public boolean regionMatches(int toffset, String other, int ooffset, int len) {
+        byte tv[] = value;
+        byte ov[] = other.value;
         // Note: toffset, ooffset, or len might be near -1>>>1.
-        if ((ooffset < 0) || (toffset < 0)
-                || (toffset > (long)ta.length - len)
-                || (ooffset > (long)pa.length - len)) {
+        if ((ooffset < 0) || (toffset < 0) ||
+             (toffset > (long)length() - len) ||
+             (ooffset > (long)other.length() - len)) {
             return false;
         }
-        while (len-- > 0) {
-            if (ta[to++] != pa[po++]) {
-                return false;
+        if (coder() == other.coder()) {
+            if (!isLatin1() && (len > 0)) {
+                toffset = toffset << 1;
+                ooffset = ooffset << 1;
+                len = len << 1;
+            }
+            while (len-- > 0) {
+                if (tv[toffset++] != ov[ooffset++]) {
+                    return false;
+                }
+            }
+        } else {
+            if (coder() == LATIN1) {
+                while (len-- > 0) {
+                    if (StringLatin1.getChar(tv, toffset++) !=
+                        StringUTF16.getChar(ov, ooffset++)) {
+                        return false;
+                    }
+                }
+            } else {
+                while (len-- > 0) {
+                    if (StringUTF16.getChar(tv, toffset++) !=
+                        StringLatin1.getChar(ov, ooffset++)) {
+                        return false;
+                    }
+                }
             }
         }
         return true;
@@ -1366,43 +1402,25 @@
      */
     public boolean regionMatches(boolean ignoreCase, int toffset,
             String other, int ooffset, int len) {
-        char[] ta = value;
-        int to = toffset;
-        char[] pa = other.value;
-        int po = ooffset;
+        if (!ignoreCase) {
+            return regionMatches(toffset, other, ooffset, len);
+        }
         // Note: toffset, ooffset, or len might be near -1>>>1.
         if ((ooffset < 0) || (toffset < 0)
-                || (toffset > (long)ta.length - len)
-                || (ooffset > (long)pa.length - len)) {
+                || (toffset > (long)length() - len)
+                || (ooffset > (long)other.length() - len)) {
             return false;
         }
-        while (len-- > 0) {
-            char c1 = ta[to++];
-            char c2 = pa[po++];
-            if (c1 == c2) {
-                continue;
-            }
-            if (ignoreCase) {
-                // If characters don't match but case may be ignored,
-                // try converting both characters to uppercase.
-                // If the results match, then the comparison scan should
-                // continue.
-                char u1 = Character.toUpperCase(c1);
-                char u2 = Character.toUpperCase(c2);
-                if (u1 == u2) {
-                    continue;
-                }
-                // Unfortunately, conversion to uppercase does not work properly
-                // for the Georgian alphabet, which has strange rules about case
-                // conversion.  So we need to make one last check before
-                // exiting.
-                if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
-                    continue;
-                }
-            }
-            return false;
+        byte tv[] = value;
+        byte ov[] = other.value;
+        if (coder() == other.coder()) {
+            return isLatin1()
+              ? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len)
+              : StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len);
         }
-        return true;
+        return isLatin1()
+              ? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)
+              : StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);
     }
 
     /**
@@ -1423,19 +1441,31 @@
      *          </pre>
      */
     public boolean startsWith(String prefix, int toffset) {
-        char[] ta = value;
-        int to = toffset;
-        char[] pa = prefix.value;
+        // Note: toffset might be near -1>>>1.
+        if (toffset < 0 || toffset > length() - prefix.length()) {
+            return false;
+        }
+        byte ta[] = value;
+        byte pa[] = prefix.value;
         int po = 0;
         int pc = pa.length;
-        // Note: toffset might be near -1>>>1.
-        if ((toffset < 0) || (toffset > ta.length - pc)) {
-            return false;
-        }
-        while (--pc >= 0) {
-            if (ta[to++] != pa[po++]) {
+        if (coder() == prefix.coder()) {
+            int to = isLatin1() ? toffset : toffset << 1;
+            while (po < pc) {
+                if (ta[to++] != pa[po++]) {
+                    return false;
+                }
+            }
+        } else {
+            if (isLatin1()) {  // && pcoder == UTF16
                 return false;
             }
+            // coder == UTF16 && pcoder == LATIN1)
+            while (po < pc) {
+                if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) {
+                    return false;
+               }
+            }
         }
         return true;
     }
@@ -1469,7 +1499,7 @@
      *          as determined by the {@link #equals(Object)} method.
      */
     public boolean endsWith(String suffix) {
-        return startsWith(suffix, value.length - suffix.value.length);
+        return startsWith(suffix, length() - suffix.length());
     }
 
     /**
@@ -1486,16 +1516,11 @@
      * @return  a hash code value for this object.
      */
     public int hashCode() {
-        int h = hash;
-        if (h == 0) {
-            for (char v : value) {
-                h = 31 * h + v;
-            }
-            if (h != 0) {
-                hash = h;
-            }
+        if (hash == 0 && value.length > 0) {
+            hash = isLatin1() ? StringLatin1.hashCode(value)
+                              : StringUTF16.hashCode(value);
         }
-        return h;
+        return hash;
     }
 
     /**
@@ -1566,45 +1591,8 @@
      *          if the character does not occur.
      */
     public int indexOf(int ch, int fromIndex) {
-        final int max = value.length;
-        if (fromIndex < 0) {
-            fromIndex = 0;
-        } else if (fromIndex >= max) {
-            // Note: fromIndex might be near -1>>>1.
-            return -1;
-        }
-
-        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
-            // handle most cases here (ch is a BMP code point or a
-            // negative value (invalid code point))
-            final char[] value = this.value;
-            for (int i = fromIndex; i < max; i++) {
-                if (value[i] == ch) {
-                    return i;
-                }
-            }
-            return -1;
-        } else {
-            return indexOfSupplementary(ch, fromIndex);
-        }
-    }
-
-    /**
-     * Handles (rare) calls of indexOf with a supplementary character.
-     */
-    private int indexOfSupplementary(int ch, int fromIndex) {
-        if (Character.isValidCodePoint(ch)) {
-            final char[] value = this.value;
-            final char hi = Character.highSurrogate(ch);
-            final char lo = Character.lowSurrogate(ch);
-            final int max = value.length - 1;
-            for (int i = fromIndex; i < max; i++) {
-                if (value[i] == hi && value[i + 1] == lo) {
-                    return i;
-                }
-            }
-        }
-        return -1;
+        return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex)
+                          : StringUTF16.indexOf(value, ch, fromIndex);
     }
 
     /**
@@ -1631,7 +1619,7 @@
      *          {@code -1} if the character does not occur.
      */
     public int lastIndexOf(int ch) {
-        return lastIndexOf(ch, value.length - 1);
+        return lastIndexOf(ch, length() - 1);
     }
 
     /**
@@ -1669,38 +1657,8 @@
      *          if the character does not occur before that point.
      */
     public int lastIndexOf(int ch, int fromIndex) {
-        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
-            // handle most cases here (ch is a BMP code point or a
-            // negative value (invalid code point))
-            final char[] value = this.value;
-            int i = Math.min(fromIndex, value.length - 1);
-            for (; i >= 0; i--) {
-                if (value[i] == ch) {
-                    return i;
-                }
-            }
-            return -1;
-        } else {
-            return lastIndexOfSupplementary(ch, fromIndex);
-        }
-    }
-
-    /**
-     * Handles (rare) calls of lastIndexOf with a supplementary character.
-     */
-    private int lastIndexOfSupplementary(int ch, int fromIndex) {
-        if (Character.isValidCodePoint(ch)) {
-            final char[] value = this.value;
-            char hi = Character.highSurrogate(ch);
-            char lo = Character.lowSurrogate(ch);
-            int i = Math.min(fromIndex, value.length - 2);
-            for (; i >= 0; i--) {
-                if (value[i] == hi && value[i + 1] == lo) {
-                    return i;
-                }
-            }
-        }
-        return -1;
+        return isLatin1() ? StringLatin1.lastIndexOf(value, ch, fromIndex)
+                          : StringUTF16.lastIndexOf(value, ch, fromIndex);
     }
 
     /**
@@ -1717,9 +1675,15 @@
      * @return  the index of the first occurrence of the specified substring,
      *          or {@code -1} if there is no such occurrence.
      */
-    @HotSpotIntrinsicCandidate
     public int indexOf(String str) {
-        return indexOf(str, 0);
+        if (coder() == str.coder()) {
+            return isLatin1() ? StringLatin1.indexOf(value, str.value)
+                              : StringUTF16.indexOf(value, str.value);
+        }
+        if (coder() == LATIN1) {  // str.coder == UTF16
+            return -1;
+        }
+        return StringUTF16.indexOfLatin1(value, str.value);
     }
 
     /**
@@ -1740,8 +1704,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int indexOf(String str, int fromIndex) {
-        return indexOf(value, 0, value.length,
-                str.value, 0, str.value.length, fromIndex);
+        return indexOf(value, coder(), length(), str, fromIndex);
     }
 
     /**
@@ -1749,68 +1712,38 @@
      * source is the character array being searched, and the target
      * is the string being searched for.
      *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   fromIndex    the index to begin searching from.
+     * @param   src       the characters being searched.
+     * @param   srcCoder  the coder of the source string.
+     * @param   srcCount  length of the source string.
+     * @param   tgtStr    the characters being searched for.
+     * @param   fromIndex the index to begin searching from.
      */
-    static int indexOf(char[] source, int sourceOffset, int sourceCount,
-            String target, int fromIndex) {
-        return indexOf(source, sourceOffset, sourceCount,
-                       target.value, 0, target.value.length,
-                       fromIndex);
-    }
+    static int indexOf(byte[] src, byte srcCoder, int srcCount,
+                       String tgtStr, int fromIndex) {
 
-    /**
-     * Code shared by String and StringBuffer to do searches. The
-     * source is the character array being searched, and the target
-     * is the string being searched for.
-     *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   targetOffset offset of the target string.
-     * @param   targetCount  count of the target string.
-     * @param   fromIndex    the index to begin searching from.
-     */
-    static int indexOf(char[] source, int sourceOffset, int sourceCount,
-            char[] target, int targetOffset, int targetCount,
-            int fromIndex) {
-        if (fromIndex >= sourceCount) {
-            return (targetCount == 0 ? sourceCount : -1);
+        byte[] tgt    = tgtStr.value;
+        byte tgtCoder = tgtStr.coder();
+        int tgtCount  = tgtStr.length();
+
+        if (fromIndex >= srcCount) {
+            return (tgtCount == 0 ? srcCount : -1);
         }
         if (fromIndex < 0) {
             fromIndex = 0;
         }
-        if (targetCount == 0) {
+        if (tgtCount == 0) {
             return fromIndex;
         }
-
-        char first = target[targetOffset];
-        int max = sourceOffset + (sourceCount - targetCount);
-
-        for (int i = sourceOffset + fromIndex; i <= max; i++) {
-            /* Look for first character. */
-            if (source[i] != first) {
-                while (++i <= max && source[i] != first);
-            }
-
-            /* Found first character, now look at the rest of v2 */
-            if (i <= max) {
-                int j = i + 1;
-                int end = j + targetCount - 1;
-                for (int k = targetOffset + 1; j < end && source[j]
-                        == target[k]; j++, k++);
-
-                if (j == end) {
-                    /* Found whole string. */
-                    return i - sourceOffset;
-                }
-            }
+        if (srcCoder == tgtCoder) {
+            return srcCoder == LATIN1
+                ? StringLatin1.indexOf(src, srcCount, tgt, tgtCount, fromIndex)
+                : StringUTF16.indexOf(src, srcCount, tgt, tgtCount, fromIndex);
         }
-        return -1;
+        if (srcCoder == LATIN1) {    //  && tgtCoder == UTF16
+            return -1;
+        }
+        // srcCoder == UTF16 && tgtCoder == LATIN1) {
+        return StringUTF16.indexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex);
     }
 
     /**
@@ -1829,7 +1762,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int lastIndexOf(String str) {
-        return lastIndexOf(str, value.length);
+        return lastIndexOf(str, length());
     }
 
     /**
@@ -1850,8 +1783,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int lastIndexOf(String str, int fromIndex) {
-        return lastIndexOf(value, 0, value.length,
-                str.value, 0, str.value.length, fromIndex);
+        return lastIndexOf(value, coder(), length(), str, fromIndex);
     }
 
     /**
@@ -1859,40 +1791,22 @@
      * source is the character array being searched, and the target
      * is the string being searched for.
      *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   fromIndex    the index to begin searching from.
+     * @param   src         the characters being searched.
+     * @param   srcCoder  coder handles the mapping between bytes/chars
+     * @param   srcCount    count of the source string.
+     * @param   tgt         the characters being searched for.
+     * @param   fromIndex   the index to begin searching from.
      */
-    static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
-            String target, int fromIndex) {
-        return lastIndexOf(source, sourceOffset, sourceCount,
-                       target.value, 0, target.value.length,
-                       fromIndex);
-    }
-
-    /**
-     * Code shared by String and StringBuffer to do searches. The
-     * source is the character array being searched, and the target
-     * is the string being searched for.
-     *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   targetOffset offset of the target string.
-     * @param   targetCount  count of the target string.
-     * @param   fromIndex    the index to begin searching from.
-     */
-    static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
-            char[] target, int targetOffset, int targetCount,
-            int fromIndex) {
+    static int lastIndexOf(byte[] src, byte srcCoder, int srcCount,
+                           String tgtStr, int fromIndex) {
+        byte[] tgt = tgtStr.value;
+        byte tgtCoder = tgtStr.coder();
+        int tgtCount = tgtStr.length();
         /*
          * Check arguments; return immediately where possible. For
          * consistency, don't check for null str.
          */
-        int rightIndex = sourceCount - targetCount;
+        int rightIndex = srcCount - tgtCount;
         if (fromIndex < 0) {
             return -1;
         }
@@ -1900,34 +1814,41 @@
             fromIndex = rightIndex;
         }
         /* Empty string always matches. */
-        if (targetCount == 0) {
+        if (tgtCount == 0) {
             return fromIndex;
         }
+        if (srcCoder == tgtCoder) {
+            return srcCoder == LATIN1
+                ? StringLatin1.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex)
+                : StringUTF16.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex);
+        }
+        if (srcCoder == LATIN1) {    // && tgtCoder == UTF16
+            return -1;
+        }
+                                     // srcCoder == UTF16 && tgtCoder == LATIN1
+        int min = tgtCount - 1;
+        int i = min + fromIndex;
+        int strLastIndex = tgtCount - 1;
 
-        int strLastIndex = targetOffset + targetCount - 1;
-        char strLastChar = target[strLastIndex];
-        int min = sourceOffset + targetCount - 1;
-        int i = min + fromIndex;
-
+        char strLastChar = (char)(tgt[strLastIndex] & 0xff);
     startSearchForLastChar:
         while (true) {
-            while (i >= min && source[i] != strLastChar) {
+            while (i >= min && StringUTF16.getChar(src, i) != strLastChar) {
                 i--;
             }
             if (i < min) {
                 return -1;
             }
             int j = i - 1;
-            int start = j - (targetCount - 1);
+            int start = j - strLastIndex;
             int k = strLastIndex - 1;
-
             while (j > start) {
-                if (source[j--] != target[k--]) {
+                if (StringUTF16.getChar(src, j--) != (tgt[k--] & 0xff)) {
                     i--;
                     continue startSearchForLastChar;
                 }
             }
-            return start - sourceOffset + 1;
+            return start + 1;
         }
     }
 
@@ -1949,17 +1870,18 @@
      *             length of this {@code String} object.
      */
     public String substring(int beginIndex) {
-        if (beginIndex <= 0) {
-            if (beginIndex < 0) {
-                throw new StringIndexOutOfBoundsException(beginIndex);
-            }
-            return this;
+        if (beginIndex < 0) {
+            throw new StringIndexOutOfBoundsException(beginIndex);
         }
-        int subLen = value.length - beginIndex;
+        int subLen = length() - beginIndex;
         if (subLen < 0) {
             throw new StringIndexOutOfBoundsException(subLen);
         }
-        return new String(value, beginIndex, subLen);
+        if (beginIndex == 0) {
+            return this;
+        }
+        return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
+                          : StringUTF16.newString(value, beginIndex, subLen);
     }
 
     /**
@@ -1985,22 +1907,14 @@
      *             {@code endIndex}.
      */
     public String substring(int beginIndex, int endIndex) {
-        if (beginIndex <= 0) {
-            if (beginIndex < 0) {
-                throw new StringIndexOutOfBoundsException(beginIndex);
-            }
-            if (endIndex == value.length) {
-                return this;
-            }
+        int length = length();
+        checkBoundsBeginEnd(beginIndex, endIndex, length);
+        int subLen = endIndex - beginIndex;
+        if (beginIndex == 0 && endIndex == length) {
+            return this;
         }
-        if (endIndex > value.length) {
-            throw new StringIndexOutOfBoundsException(endIndex);
-        }
-        int subLen = endIndex - beginIndex;
-        if (subLen < 0) {
-            throw new StringIndexOutOfBoundsException(subLen);
-        }
-        return new String(value, beginIndex, subLen);
+        return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
+                          : StringUTF16.newString(value, beginIndex, subLen);
     }
 
     /**
@@ -2057,14 +1971,23 @@
      *          characters followed by the string argument's characters.
      */
     public String concat(String str) {
-        int otherLen = str.length();
-        if (otherLen == 0) {
+        int olen = str.length();
+        if (olen == 0) {
             return this;
         }
-        int len = value.length;
-        char[] buf = Arrays.copyOf(value, len + otherLen);
-        str.getChars(buf, len);
-        return new String(buf, true);
+        if (coder() == str.coder()) {
+            byte[] val = this.value;
+            byte[] oval = str.value;
+            int len = val.length + oval.length;
+            byte[] buf = Arrays.copyOf(val, len);
+            System.arraycopy(oval, 0, buf, val.length, oval.length);
+            return new String(buf, coder);
+        }
+        int len = length();
+        byte[] buf = StringUTF16.newBytesFor(len + olen);
+        getBytes(buf, 0, UTF16);
+        str.getBytes(buf, len, UTF16);
+        return new String(buf, UTF16);
     }
 
     /**
@@ -2098,26 +2021,10 @@
      */
     public String replace(char oldChar, char newChar) {
         if (oldChar != newChar) {
-            char[] val = value; /* avoid getfield opcode */
-            int len = val.length;
-            int i = -1;
-
-            while (++i < len) {
-                if (val[i] == oldChar) {
-                    break;
-                }
-            }
-            if (i < len) {
-                char[] buf = new char[len];
-                for (int j = 0; j < i; j++) {
-                    buf[j] = val[j];
-                }
-                while (i < len) {
-                    char c = val[i];
-                    buf[i] = (c == oldChar) ? newChar : c;
-                    i++;
-                }
-                return new String(buf, true);
+            String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar)
+                                    : StringUTF16.replace(value, oldChar, newChar);
+            if (ret != null) {
+                return ret;
             }
         }
         return this;
@@ -2269,29 +2176,27 @@
      * @since 1.5
      */
     public String replace(CharSequence target, CharSequence replacement) {
-        String starget = target.toString();
-        String srepl = replacement.toString();
-        int j = indexOf(starget);
+        String tgtStr = target.toString();
+        String replStr = replacement.toString();
+        int j = indexOf(tgtStr);
         if (j < 0) {
             return this;
         }
-        int targLen = starget.length();
-        int targLen1 = Math.max(targLen, 1);
-        final char[] value = this.value;
-        final char[] replValue = srepl.value;
-        int newLenHint = value.length - targLen + replValue.length;
+        int tgtLen = tgtStr.length();
+        int tgtLen1 = Math.max(tgtLen, 1);
+        int thisLen = length();
+
+        int newLenHint = thisLen - tgtLen + replStr.length();
         if (newLenHint < 0) {
             throw new OutOfMemoryError();
         }
         StringBuilder sb = new StringBuilder(newLenHint);
         int i = 0;
         do {
-            sb.append(value, i, j - i)
-                    .append(replValue);
-            i = j + targLen;
-        } while (j < value.length && (j = indexOf(starget, j + targLen1)) > 0);
-
-        return sb.append(value, i, value.length - i).toString();
+            sb.append(this, i, j).append(replStr);
+            i = j + tgtLen;
+        } while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);
+        return sb.append(this, i, thisLen).toString();
     }
 
     /**
@@ -2388,7 +2293,7 @@
             the second is not the ascii digit or ascii letter.
          */
         char ch = 0;
-        if (((regex.value.length == 1 &&
+        if (((regex.length() == 1 &&
              ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
              (regex.length() == 2 &&
               regex.charAt(0) == '\\' &&
@@ -2408,8 +2313,9 @@
                     off = next + 1;
                 } else {    // last one
                     //assert (list.size() == limit - 1);
-                    list.add(substring(off, value.length));
-                    off = value.length;
+                    int last = length();
+                    list.add(substring(off, last));
+                    off = last;
                     break;
                 }
             }
@@ -2419,7 +2325,7 @@
 
             // Add remaining segment
             if (!limited || list.size() < limit)
-                list.add(substring(off, value.length));
+                list.add(substring(off, length()));
 
             // Construct result
             int resultSize = list.size();
@@ -2613,95 +2519,8 @@
      * @since   1.1
      */
     public String toLowerCase(Locale locale) {
-        if (locale == null) {
-            throw new NullPointerException();
-        }
-        int first;
-        boolean hasSurr = false;
-        final int len = value.length;
-
-        // Now check if there are any characters that need to be changed, or are surrogate
-        for (first = 0 ; first < len; first++) {
-            int cp = (int)value[first];
-            if (Character.isSurrogate((char)cp)) {
-                hasSurr = true;
-                break;
-            }
-            if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
-                break;
-            }
-        }
-        if (first == len)
-            return this;
-        char[] result = new char[len];
-        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
-                                                       // lowerCase characters.
-        String lang = locale.getLanguage();
-        if (lang == "tr" || lang == "az" || lang == "lt") {
-            return toLowerCaseEx(result, first, locale, true);
-        }
-        if (hasSurr) {
-            return toLowerCaseEx(result, first, locale, false);
-        }
-        for (int i = first; i < len; i++) {
-            int cp = (int)value[i];
-            if (cp == '\u03A3' ||                       // GREEK CAPITAL LETTER SIGMA
-                Character.isSurrogate((char)cp)) {
-                return toLowerCaseEx(result, i, locale, false);
-            }
-            if (cp == '\u0130') {                       // LATIN CAPITAL LETTER I WITH DOT ABOVE
-                return toLowerCaseEx(result, i, locale, true);
-            }
-            cp = Character.toLowerCase(cp);
-            if (!Character.isBmpCodePoint(cp)) {
-                return toLowerCaseEx(result, i, locale, false);
-            }
-            result[i] = (char)cp;
-        }
-        return new String(result, true);
-    }
-
-    private String toLowerCaseEx(char[] result, int first, Locale locale, boolean localeDependent) {
-        int resultOffset = first;
-        int srcCount;
-        for (int i = first; i < value.length; i += srcCount) {
-            int srcChar = (int)value[i];
-            int lowerChar;
-            char[] lowerCharArray;
-            srcCount = 1;
-            if (Character.isSurrogate((char)srcChar)) {
-                srcChar = codePointAt(i);
-                srcCount = Character.charCount(srcChar);
-            }
-            if (localeDependent || srcChar == '\u03A3') { // GREEK CAPITAL LETTER SIGMA
-                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
-            } else {
-                lowerChar = Character.toLowerCase(srcChar);
-            }
-            if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
-                result[resultOffset++] = (char)lowerChar;
-            } else {
-                if (lowerChar == Character.ERROR) {
-                    lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
-                } else if (srcCount == 2) {
-                    resultOffset += Character.toChars(lowerChar, result, resultOffset);
-                    continue;
-                } else {
-                    lowerCharArray = Character.toChars(lowerChar);
-                }
-                /* Grow result if needed */
-                int mapLen = lowerCharArray.length;
-                if (mapLen > srcCount) {
-                    char[] result2 = new char[result.length + mapLen - srcCount];
-                    System.arraycopy(result, 0, result2, 0, resultOffset);
-                    result = result2;
-                }
-                for (int x = 0; x < mapLen; ++x) {
-                    result[resultOffset++] = lowerCharArray[x];
-                }
-            }
-        }
-        return new String(result, 0, resultOffset);
+        return isLatin1() ? StringLatin1.toLowerCase(this, value, locale)
+                          : StringUTF16.toLowerCase(this, value, locale);
     }
 
     /**
@@ -2776,98 +2595,8 @@
      * @since   1.1
      */
     public String toUpperCase(Locale locale) {
-        if (locale == null) {
-            throw new NullPointerException();
-        }
-        int first;
-        boolean hasSurr = false;
-        final int len = value.length;
-
-        // Now check if there are any characters that need to be changed, or are surrogate
-        for (first = 0 ; first < len; first++ ) {
-            int cp = (int)value[first];
-            if (Character.isSurrogate((char)cp)) {
-                hasSurr = true;
-                break;
-            }
-            if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
-                break;
-            }
-        }
-        if (first == len) {
-            return this;
-        }
-        char[] result = new char[len];
-        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
-                                                       // upperCase characters.
-        String lang = locale.getLanguage();
-        if (lang == "tr" || lang == "az" || lang == "lt") {
-            return toUpperCaseEx(result, first, locale, true);
-        }
-        if (hasSurr) {
-            return toUpperCaseEx(result, first, locale, false);
-        }
-        for (int i = first; i < len; i++) {
-            int cp = (int)value[i];
-            if (Character.isSurrogate((char)cp)) {
-                return toUpperCaseEx(result, i, locale, false);
-            }
-            cp = Character.toUpperCaseEx(cp);
-            if (!Character.isBmpCodePoint(cp)) {    // Character.ERROR is not bmp
-                return toUpperCaseEx(result, i, locale, false);
-            }
-            result[i] = (char)cp;
-        }
-        return new String(result, true);
-    }
-
-    private String toUpperCaseEx(char[] result, int first, Locale locale,
-                                 boolean localeDependent) {
-        int resultOffset = first;
-        int srcCount;
-        for (int i = first; i < value.length; i += srcCount) {
-            int srcChar = (int)value[i];
-            int upperChar;
-            char[] upperCharArray;
-            srcCount = 1;
-            if (Character.isSurrogate((char)srcChar)) {
-                srcChar = codePointAt(i);
-                srcCount = Character.charCount(srcChar);
-            }
-            if (localeDependent) {
-                upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale);
-            } else {
-                upperChar = Character.toUpperCaseEx(srcChar);
-            }
-            if (Character.isBmpCodePoint(upperChar)) {
-                result[resultOffset++] = (char)upperChar;
-            } else {
-                if (upperChar == Character.ERROR) {
-                    if (localeDependent) {
-                        upperCharArray =
-                            ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale);
-                    } else {
-                        upperCharArray = Character.toUpperCaseCharArray(srcChar);
-                    }
-                } else if (srcCount == 2) {
-                    resultOffset += Character.toChars(upperChar, result, resultOffset);
-                    continue;
-                } else {
-                    upperCharArray = Character.toChars(upperChar);
-                }
-                /* Grow result if needed */
-                int mapLen = upperCharArray.length;
-                if (mapLen > srcCount) {
-                    char[] result2 = new char[result.length + mapLen - srcCount];
-                    System.arraycopy(result, 0, result2, 0, resultOffset);
-                    result = result2;
-                 }
-                 for (int x = 0; x < mapLen; ++x) {
-                    result[resultOffset++] = upperCharArray[x];
-                 }
-            }
-        }
-        return new String(result, 0, resultOffset);
+        return isLatin1() ? StringLatin1.toUpperCase(this, value, locale)
+                          : StringUTF16.toUpperCase(this, value, locale);
     }
 
     /**
@@ -2925,17 +2654,9 @@
      *          trailing white space.
      */
     public String trim() {
-        char[] val = value;    /* avoid getfield opcode */
-        int end = val.length;
-        int beg = 0;
-
-        while ((beg < end) && (val[beg] <= ' ')) {
-            beg++;
-        }
-        while ((beg < end) && (val[end - 1] <= ' ')) {
-            end--;
-        }
-        return substring(beg, end);
+        String ret = isLatin1() ? StringLatin1.trim(value)
+                                : StringUTF16.trim(value);
+        return ret == null ? this : ret;
     }
 
     /**
@@ -2947,63 +2668,6 @@
         return this;
     }
 
-    static class IntCharArraySpliterator implements Spliterator.OfInt {
-        private final char[] array;
-        private int index;        // current index, modified on advance/split
-        private final int fence;  // one past last index
-        private final int cs;
-
-        IntCharArraySpliterator(char[] array, int acs) {
-            this(array, 0, array.length, acs);
-        }
-
-        IntCharArraySpliterator(char[] array, int origin, int fence, int acs) {
-            this.array = array;
-            this.index = origin;
-            this.fence = fence;
-            this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
-                      | Spliterator.SUBSIZED;
-        }
-
-        @Override
-        public OfInt trySplit() {
-            int lo = index, mid = (lo + fence) >>> 1;
-            return (lo >= mid)
-                   ? null
-                   : new IntCharArraySpliterator(array, lo, index = mid, cs);
-        }
-
-        @Override
-        public void forEachRemaining(IntConsumer action) {
-            char[] a; int i, hi; // hoist accesses and checks from loop
-            if (action == null)
-                throw new NullPointerException();
-            if ((a = array).length >= (hi = fence) &&
-                (i = index) >= 0 && i < (index = hi)) {
-                do { action.accept(a[i]); } while (++i < hi);
-            }
-        }
-
-        @Override
-        public boolean tryAdvance(IntConsumer action) {
-            if (action == null)
-                throw new NullPointerException();
-            if (index >= 0 && index < fence) {
-                action.accept(array[index++]);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public long estimateSize() { return (long)(fence - index); }
-
-        @Override
-        public int characteristics() {
-            return cs;
-        }
-    }
-
     /**
      * Returns a stream of {@code int} zero-extending the {@code char} values
      * from this sequence.  Any char which maps to a <a
@@ -3016,93 +2680,11 @@
     @Override
     public IntStream chars() {
         return StreamSupport.intStream(
-                new IntCharArraySpliterator(value, Spliterator.IMMUTABLE), false);
+            isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
+                       : new StringUTF16.CharsSpliterator(value, Spliterator.IMMUTABLE),
+            false);
     }
 
-    static class CodePointsSpliterator implements Spliterator.OfInt {
-        private final char[] array;
-        private int index;        // current index, modified on advance/split
-        private final int fence;  // one past last index
-        private final int cs;
-
-        CodePointsSpliterator(char[] array, int acs) {
-            this(array, 0, array.length, acs);
-        }
-
-        CodePointsSpliterator(char[] array, int origin, int fence, int acs) {
-            this.array = array;
-            this.index = origin;
-            this.fence = fence;
-            this.cs = acs | Spliterator.ORDERED;
-        }
-
-        @Override
-        public OfInt trySplit() {
-            int lo = index, mid = (lo + fence) >>> 1;
-            if (lo >= mid)
-                return null;
-
-            int midOneLess;
-            // If the mid-point intersects a surrogate pair
-            if (Character.isLowSurrogate(array[mid]) &&
-                Character.isHighSurrogate(array[midOneLess = (mid -1)])) {
-                // If there is only one pair it cannot be split
-                if (lo >= midOneLess)
-                    return null;
-                // Shift the mid-point to align with the surrogate pair
-                return new CodePointsSpliterator(array, lo, index = midOneLess, cs);
-            }
-            return new CodePointsSpliterator(array, lo, index = mid, cs);
-        }
-
-        @Override
-        public void forEachRemaining(IntConsumer action) {
-            char[] a; int i, hi; // hoist accesses and checks from loop
-            if (action == null)
-                throw new NullPointerException();
-            if ((a = array).length >= (hi = fence) &&
-                (i = index) >= 0 && i < (index = hi)) {
-                do {
-                    i = advance(a, i, hi, action);
-                } while (i < hi);
-            }
-        }
-
-        @Override
-        public boolean tryAdvance(IntConsumer action) {
-            if (action == null)
-                throw new NullPointerException();
-            if (index >= 0 && index < fence) {
-                index = advance(array, index, fence, action);
-                return true;
-            }
-            return false;
-        }
-
-        // Advance one code point from the index, i, and return the next
-        // index to advance from
-        private static int advance(char[] a, int i, int hi, IntConsumer action) {
-            char c1 = a[i++];
-            int cp = c1;
-            if (Character.isHighSurrogate(c1) && i < hi) {
-                char c2 = a[i];
-                if (Character.isLowSurrogate(c2)) {
-                    i++;
-                    cp = Character.toCodePoint(c1, c2);
-                }
-            }
-            action.accept(cp);
-            return i;
-        }
-
-        @Override
-        public long estimateSize() { return (long)(fence - index); }
-
-        @Override
-        public int characteristics() {
-            return cs;
-        }
-    }
 
     /**
      * Returns a stream of code point values from this sequence.  Any surrogate
@@ -3118,7 +2700,9 @@
     @Override
     public IntStream codePoints() {
         return StreamSupport.intStream(
-                new CodePointsSpliterator(value, Spliterator.IMMUTABLE), false);
+            isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
+                       : new StringUTF16.CodePointsSpliterator(value, Spliterator.IMMUTABLE),
+            false);
     }
 
     /**
@@ -3129,10 +2713,8 @@
      *          the character sequence represented by this string.
      */
     public char[] toCharArray() {
-        // Cannot use Arrays.copyOf because of class initialization order issues
-        char[] result = new char[value.length];
-        System.arraycopy(value, 0, result, 0, value.length);
-        return result;
+        return isLatin1() ? StringLatin1.toChars(value)
+                          : StringUTF16.toChars(value);
     }
 
     /**
@@ -3315,7 +2897,10 @@
      *          as its single character the argument {@code c}.
      */
     public static String valueOf(char c) {
-        return new String(new char[]{c}, true);
+        if (COMPACT_STRINGS && StringLatin1.canEncode(c)) {
+            return new String(StringLatin1.toBytes(c), LATIN1);
+        }
+        return new String(StringUTF16.toBytes(c), UTF16);
     }
 
     /**
@@ -3398,4 +2983,145 @@
      *          guaranteed to be from a pool of unique strings.
      */
     public native String intern();
+
+    ////////////////////////////////////////////////////////////////
+
+    /**
+     * Copy character bytes from this string into dst starting at dstBegin.
+     * This method doesn't perform any range checking.
+     *
+     * Invoker guarantees: dst is in UTF16 (inflate itself for asb), if two
+     * coders are different, and dst is big enough (range check)
+     *
+     * @param dstBegin  the char index, not offset of byte[]
+     * @param coder     the coder of dst[]
+     */
+    void getBytes(byte dst[], int dstBegin, byte coder) {
+        if (coder() == coder) {
+            System.arraycopy(value, 0, dst, dstBegin << coder, value.length);
+        } else {    // this.coder == LATIN && coder == UTF16
+            StringLatin1.inflate(value, 0, dst, dstBegin, value.length);
+        }
+    }
+
+    /*
+     * Package private constructor. Trailing Void argument is there for
+     * disambiguating it against other (public) constructors.
+     *
+     * Stores the char[] value into a byte[] that each byte represents
+     * the8 low-order bits of the corresponding character, if the char[]
+     * contains only latin1 character. Or a byte[] that stores all
+     * characters in their byte sequences defined by the {@code StringUTF16}.
+     */
+    String(char[] value, int off, int len, Void sig) {
+        if (len == 0) {
+            this.value = "".value;
+            this.coder = "".coder;
+            return;
+        }
+        if (COMPACT_STRINGS) {
+            byte[] val = StringUTF16.compress(value, off, len);
+            if (val != null) {
+                this.value = val;
+                this.coder = LATIN1;
+                return;
+            }
+        }
+        this.coder = UTF16;
+        this.value = StringUTF16.toBytes(value, off, len);
+    }
+
+    /*
+     * Package private constructor. Trailing Void argument is there for
+     * disambiguating it against other (public) constructors.
+     */
+    String(AbstractStringBuilder asb, Void sig) {
+        byte[] val = asb.getValue();
+        int length = asb.length();
+        if (asb.isLatin1()) {
+            this.coder = LATIN1;
+            this.value = Arrays.copyOfRange(val, 0, length);
+        } else {
+            if (COMPACT_STRINGS) {
+                byte[] buf = StringUTF16.compress(val, 0, length);
+                if (buf != null) {
+                    this.coder = LATIN1;
+                    this.value = buf;
+                    return;
+                }
+            }
+            this.coder = UTF16;
+            this.value = Arrays.copyOfRange(val, 0, length << 1);
+        }
+    }
+
+   /*
+    * Package private constructor which shares value array for speed.
+    */
+    String(byte[] value, byte coder) {
+        this.value = value;
+        this.coder = coder;
+    }
+
+    byte coder() {
+        return COMPACT_STRINGS ? coder : UTF16;
+    }
+
+    private boolean isLatin1() {
+        return COMPACT_STRINGS && coder == LATIN1;
+    }
+
+    static final byte LATIN1 = 0;
+    static final byte UTF16  = 1;
+
+    /*
+     * StringIndexOutOfBoundsException  if {@code index} is
+     * negative or greater than or equal to {@code length}.
+     */
+    static void checkIndex(int index, int length) {
+        if (index < 0 || index >= length) {
+            throw new StringIndexOutOfBoundsException("index " + index);
+        }
+    }
+
+    /*
+     * StringIndexOutOfBoundsException  if {@code offset}
+     * is negative or greater than {@code length}.
+     */
+    static void checkOffset(int offset, int length) {
+        if (offset < 0 || offset > length) {
+            throw new StringIndexOutOfBoundsException("offset " + offset +
+                                                      ",length " + length);
+        }
+    }
+
+    /*
+     * Check {@code offset}, {@code count} against {@code 0} and {@code length}
+     * bounds.
+     *
+     * @throws  StringIndexOutOfBoundsException
+     *          If {@code offset} is negative, {@code count} is negative,
+     *          or {@code offset} is greater than {@code length - count}
+     */
+    private static void checkBoundsOffCount(int offset, int count, int length) {
+        if (offset < 0 || count < 0 || offset > length - count) {
+            throw new StringIndexOutOfBoundsException(
+                "offset " + offset + ", count " + count + ", length " + length);
+        }
+    }
+
+    /*
+     * Check {@code begin}, {@code end} against {@code 0} and {@code length}
+     * bounds.
+     *
+     * @throws  StringIndexOutOfBoundsException
+     *          If {@code begin} is negative, {@code begin} is greater than
+     *          {@code end}, or {@code end} is greater than {@code length}.
+     */
+    private static void checkBoundsBeginEnd(int begin, int end, int length) {
+        if (begin < 0 || begin > end || end > length) {
+            throw new StringIndexOutOfBoundsException(
+                "begin " + begin + ", end " + end + ", length " + length);
+        }
+    }
 }
--- a/src/java.base/share/classes/java/lang/StringBuffer.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/lang/StringBuffer.java	Mon Nov 09 20:37:05 2015 -0800
@@ -104,7 +104,7 @@
      * A cache of the last value returned by toString. Cleared
      * whenever the StringBuffer is modified.
      */
-    private transient char[] toStringCache;
+    private transient String toStringCache;
 
     /** use serialVersionUID from JDK 1.0.2 for interoperability */
     static final long serialVersionUID = 3388685877147921107L;
@@ -169,15 +169,13 @@
 
     @Override
     public synchronized int capacity() {
-        return value.length;
+        return super.capacity();
     }
 
 
     @Override
     public synchronized void ensureCapacity(int minimumCapacity) {
-        if (minimumCapacity > value.length) {
-            expandCapacity(minimumCapacity);
-        }
+        super.ensureCapacity(minimumCapacity);
     }
 
     /**
@@ -204,9 +202,7 @@
      */
     @Override
     public synchronized char charAt(int index) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        return value[index];
+        return super.charAt(index);
     }
 
     /**
@@ -261,10 +257,8 @@
      */
     @Override
     public synchronized void setCharAt(int index, char ch) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
         toStringCache = null;
-        value[index] = ch;
+        super.setCharAt(index, ch);
     }
 
     @Override
@@ -680,9 +674,11 @@
     @HotSpotIntrinsicCandidate
     public synchronized String toString() {
         if (toStringCache == null) {
-            toStringCache = Arrays.copyOfRange(value, 0, count);
+            return toStringCache =
+                    isLatin1() ? StringLatin1.newString(value, 0, count)
+                               : StringUTF16.newString(value, 0, count);
         }
-        return new String(toStringCache, true);
+        return new String(toStringCache);
     }
 
     /**
@@ -710,7 +706,13 @@
     private synchronized void writeObject(java.io.ObjectOutputStream s)
         throws java.io.IOException {
         java.io.ObjectOutputStream.PutField fields = s.putFields();
-        fields.put("value", value);
+        char[] val = new char[capacity()];
+        if (isLatin1()) {
+            StringLatin1.getChars(value, 0, count, val, 0);
+        } else {
+            StringUTF16.getChars(value, 0, count, val, 0);
+        }
+        fields.put("value", val);
         fields.put("count", count);
         fields.put("shared", false);
         s.writeFields();
@@ -723,7 +725,12 @@
     private void readObject(java.io.ObjectInputStream s)
         throws java.io.IOException, ClassNotFoundException {
         java.io.ObjectInputStream.GetField fields = s.readFields();
-        value = (char[])fields.get("value", null);
+        char[] val = (char[])fields.get("value", null);
+        initBytes(val, 0, val.length);
         count = fields.get("count", 0);
     }
+
+    protected synchronized void getBytes(byte dst[], int dstBegin, byte coder) {
+        super.getBytes(dst, dstBegin, coder);
+    }
 }
--- a/src/java.base/share/classes/java/lang/StringBuilder.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/lang/StringBuilder.java	Mon Nov 09 20:37:05 2015 -0800
@@ -412,7 +412,8 @@
     @HotSpotIntrinsicCandidate
     public String toString() {
         // Create a copy, don't share the array
-        return new String(value, 0, count);
+        return isLatin1() ? StringLatin1.newString(value, 0, count)
+                          : StringUTF16.newStringSB(value, 0, count);
     }
 
     /**
@@ -430,7 +431,13 @@
         throws java.io.IOException {
         s.defaultWriteObject();
         s.writeInt(count);
-        s.writeObject(value);
+        char[] val = new char[capacity()];
+        if (isLatin1()) {
+            StringLatin1.getChars(value, 0, count, val, 0);
+        } else {
+            StringUTF16.getChars(value, 0, count, val, 0);
+        }
+        s.writeObject(val);
     }
 
     /**
@@ -441,7 +448,8 @@
         throws java.io.IOException, ClassNotFoundException {
         s.defaultReadObject();
         count = s.readInt();
-        value = (char[]) s.readObject();
+        char[] val = (char[]) s.readObject();
+        initBytes(val, 0, val.length);
     }
 
 }
--- a/src/java.base/share/classes/java/lang/StringCoding.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/lang/StringCoding.java	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,11 +38,19 @@
 import java.nio.charset.IllegalCharsetNameException;
 import java.nio.charset.UnsupportedCharsetException;
 import java.util.Arrays;
+import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.misc.MessageUtils;
 import sun.nio.cs.HistoricallyNamedCharset;
 import sun.nio.cs.ArrayDecoder;
 import sun.nio.cs.ArrayEncoder;
 
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+import static java.lang.String.COMPACT_STRINGS;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 /**
  * Utility class for string encoding and decoding.
  */
@@ -72,23 +80,13 @@
 
     // Trim the given byte array to the given length
     //
-    private static byte[] safeTrim(byte[] ba, int len, Charset cs, boolean isTrusted) {
+    private static byte[] safeTrim(byte[] ba, int len, boolean isTrusted) {
         if (len == ba.length && (isTrusted || System.getSecurityManager() == null))
             return ba;
         else
             return Arrays.copyOf(ba, len);
     }
 
-    // Trim the given char array to the given length
-    //
-    private static char[] safeTrim(char[] ca, int len,
-                                   Charset cs, boolean isTrusted) {
-        if (len == ca.length && (isTrusted || System.getSecurityManager() == null))
-            return ca;
-        else
-            return Arrays.copyOf(ca, len);
-    }
-
     private static int scale(int len, float expansionFactor) {
         // We need to perform double, not float, arithmetic; otherwise
         // we lose low order bits when len is larger than 2**24.
@@ -117,21 +115,64 @@
         }
     }
 
+    static class Result {
+        byte[] value;
+        byte coder;
+
+        Result with() {
+            coder = COMPACT_STRINGS ? LATIN1 : UTF16;
+            value = new byte[0];
+            return this;
+        }
+
+        Result with(char[] val, int off, int len) {
+            if (String.COMPACT_STRINGS) {
+                byte[] bs = StringUTF16.compress(val, off, len);
+                if (bs != null) {
+                    value = bs;
+                    coder = LATIN1;
+                    return this;
+                }
+            }
+            coder = UTF16;
+            value = StringUTF16.toBytes(val, off, len);
+            return this;
+        }
+
+        Result with(byte[] val, byte coder) {
+            this.coder = coder;
+            value = val;
+            return this;
+        }
+    }
+
+    @HotSpotIntrinsicCandidate
+    private static boolean hasNegatives(byte[] ba, int off, int len) {
+        for (int i = off; i < off + len; i++) {
+            if (ba[i] < 0) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     // -- Decoding --
-    private static class StringDecoder {
+    static class StringDecoder {
         private final String requestedCharsetName;
         private final Charset cs;
+        private final boolean isASCIICompatible;
         private final CharsetDecoder cd;
-        private final boolean isTrusted;
+        protected final Result result;
 
-        private StringDecoder(Charset cs, String rcn) {
+        StringDecoder(Charset cs, String rcn) {
             this.requestedCharsetName = rcn;
             this.cs = cs;
             this.cd = cs.newDecoder()
                 .onMalformedInput(CodingErrorAction.REPLACE)
                 .onUnmappableCharacter(CodingErrorAction.REPLACE);
-            this.isTrusted = (cs.getClass().getClassLoader0() == null);
+            this.result = new Result();
+            this.isASCIICompatible = (cd instanceof ArrayDecoder) &&
+                    ((ArrayDecoder)cd).isASCIICompatible();
         }
 
         String charsetName() {
@@ -144,36 +185,58 @@
             return requestedCharsetName;
         }
 
-        char[] decode(byte[] ba, int off, int len) {
+        Result decode(byte[] ba, int off, int len) {
+            if (len == 0) {
+                return result.with();
+            }
+            // fastpath for ascii compatible
+            if (isASCIICompatible && !hasNegatives(ba, off, len)) {
+                if (COMPACT_STRINGS) {
+                    return result.with(Arrays.copyOfRange(ba, off, off + len),
+                                      LATIN1);
+                } else {
+                    return result.with(StringLatin1.inflate(ba, off, len), UTF16);
+                }
+            }
             int en = scale(len, cd.maxCharsPerByte());
             char[] ca = new char[en];
-            if (len == 0)
-                return ca;
             if (cd instanceof ArrayDecoder) {
                 int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
-                return safeTrim(ca, clen, cs, isTrusted);
+                return result.with(ca, 0, clen);
+            }
+            cd.reset();
+            ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
+            CharBuffer cb = CharBuffer.wrap(ca);
+            try {
+                CoderResult cr = cd.decode(bb, cb, true);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+                cr = cd.flush(cb);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+            } catch (CharacterCodingException x) {
+                // Substitution is always enabled,
+                // so this shouldn't happen
+                throw new Error(x);
+            }
+            return result.with(ca, 0, cb.position());
+        }
+    }
+
+    private static class StringDecoder8859_1 extends StringDecoder {
+        StringDecoder8859_1(Charset cs, String rcn) {
+            super(cs, rcn);
+        }
+        Result decode(byte[] ba, int off, int len) {
+            if (COMPACT_STRINGS) {
+                return result.with(Arrays.copyOfRange(ba, off, off + len), LATIN1);
             } else {
-                cd.reset();
-                ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
-                CharBuffer cb = CharBuffer.wrap(ca);
-                try {
-                    CoderResult cr = cd.decode(bb, cb, true);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                    cr = cd.flush(cb);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                } catch (CharacterCodingException x) {
-                    // Substitution is always enabled,
-                    // so this shouldn't happen
-                    throw new Error(x);
-                }
-                return safeTrim(ca, cb.position(), cs, isTrusted);
+                return result.with(StringLatin1.inflate(ba, off, len), UTF16);
             }
         }
     }
 
-    static char[] decode(String charsetName, byte[] ba, int off, int len)
+    static Result decode(String charsetName, byte[] ba, int off, int len)
         throws UnsupportedEncodingException
     {
         StringDecoder sd = deref(decoder);
@@ -183,8 +246,15 @@
             sd = null;
             try {
                 Charset cs = lookupCharset(csn);
-                if (cs != null)
-                    sd = new StringDecoder(cs, csn);
+                if (cs != null) {
+                    if (cs == UTF_8) {
+                        sd = new StringDecoderUTF8(cs, csn);
+                    } else if (cs == ISO_8859_1) {
+                        sd = new StringDecoder8859_1(cs, csn);
+                    } else {
+                        sd = new StringDecoder(cs, csn);
+                    }
+                }
             } catch (IllegalCharsetNameException x) {}
             if (sd == null)
                 throw new UnsupportedEncodingException(csn);
@@ -193,7 +263,7 @@
         return sd.decode(ba, off, len);
     }
 
-    static char[] decode(Charset cs, byte[] ba, int off, int len) {
+    static Result decode(Charset cs, byte[] ba, int off, int len) {
         // (1)We never cache the "external" cs, the only benefit of creating
         // an additional StringDe/Encoder object to wrap it is to share the
         // de/encode() method. These SD/E objects are short-lived, the young-gen
@@ -210,44 +280,57 @@
         // check (... && (isTrusted || SM == null || getClassLoader0())) in trim
         // but it then can be argued that the SM is null when the operation
         // is started...
+        if (cs == UTF_8) {
+            return StringDecoderUTF8.decode(ba, off, len, new Result());
+        }
         CharsetDecoder cd = cs.newDecoder();
+        // ascii fastpath
+        if (cs == ISO_8859_1 || ((cd instanceof ArrayDecoder) &&
+                                 ((ArrayDecoder)cd).isASCIICompatible() &&
+                                 !hasNegatives(ba, off, len))) {
+             if (COMPACT_STRINGS) {
+                 return new Result().with(Arrays.copyOfRange(ba, off, off + len),
+                                          LATIN1);
+             } else {
+                 return new Result().with(StringLatin1.inflate(ba, off, len), UTF16);
+             }
+        }
         int en = scale(len, cd.maxCharsPerByte());
-        char[] ca = new char[en];
-        if (len == 0)
-            return ca;
-        boolean isTrusted = false;
-        if (System.getSecurityManager() != null) {
-            if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
-                ba =  Arrays.copyOfRange(ba, off, off + len);
-                off = 0;
-            }
+        if (len == 0) {
+            return new Result().with();
+        }
+        if (System.getSecurityManager() != null &&
+            cs.getClass().getClassLoader0() != null) {
+            ba =  Arrays.copyOfRange(ba, off, off + len);
+            off = 0;
         }
         cd.onMalformedInput(CodingErrorAction.REPLACE)
           .onUnmappableCharacter(CodingErrorAction.REPLACE)
           .reset();
+
+        char[] ca = new char[en];
         if (cd instanceof ArrayDecoder) {
             int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
-            return safeTrim(ca, clen, cs, isTrusted);
-        } else {
-            ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
-            CharBuffer cb = CharBuffer.wrap(ca);
-            try {
-                CoderResult cr = cd.decode(bb, cb, true);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-                cr = cd.flush(cb);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-            } catch (CharacterCodingException x) {
-                // Substitution is always enabled,
-                // so this shouldn't happen
-                throw new Error(x);
-            }
-            return safeTrim(ca, cb.position(), cs, isTrusted);
+            return new Result().with(ca, 0, clen);
         }
+        ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
+        CharBuffer cb = CharBuffer.wrap(ca);
+        try {
+            CoderResult cr = cd.decode(bb, cb, true);
+            if (!cr.isUnderflow())
+                cr.throwException();
+            cr = cd.flush(cb);
+            if (!cr.isUnderflow())
+                cr.throwException();
+        } catch (CharacterCodingException x) {
+            // Substitution is always enabled,
+            // so this shouldn't happen
+            throw new Error(x);
+        }
+        return new Result().with(ca, 0, cb.position());
     }
 
-    static char[] decode(byte[] ba, int off, int len) {
+    static Result decode(byte[] ba, int off, int len) {
         String csn = Charset.defaultCharset().name();
         try {
             // use charset name decode() variant which provides caching.
@@ -273,6 +356,7 @@
     private static class StringEncoder {
         private Charset cs;
         private CharsetEncoder ce;
+        private final boolean isASCIICompatible;
         private final String requestedCharsetName;
         private final boolean isTrusted;
 
@@ -283,6 +367,8 @@
                 .onMalformedInput(CodingErrorAction.REPLACE)
                 .onUnmappableCharacter(CodingErrorAction.REPLACE);
             this.isTrusted = (cs.getClass().getClassLoader0() == null);
+            this.isASCIICompatible = (ce instanceof ArrayEncoder) &&
+                    ((ArrayEncoder)ce).isASCIICompatible();
         }
 
         String charsetName() {
@@ -295,36 +381,186 @@
             return requestedCharsetName;
         }
 
-        byte[] encode(char[] ca, int off, int len) {
+        byte[] encode(byte coder, byte[] val) {
+            // fastpath for ascii compatible
+            if (coder == LATIN1 && isASCIICompatible &&
+                !hasNegatives(val, 0, val.length)) {
+                return Arrays.copyOf(val, val.length);
+            }
+            int len = val.length >> coder;  // assume LATIN1=0/UTF16=1;
             int en = scale(len, ce.maxBytesPerChar());
             byte[] ba = new byte[en];
-            if (len == 0)
+            if (len == 0) {
                 return ba;
+            }
             if (ce instanceof ArrayEncoder) {
-                int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
-                return safeTrim(ba, blen, cs, isTrusted);
-            } else {
-                ce.reset();
-                ByteBuffer bb = ByteBuffer.wrap(ba);
-                CharBuffer cb = CharBuffer.wrap(ca, off, len);
-                try {
-                    CoderResult cr = ce.encode(cb, bb, true);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                    cr = ce.flush(bb);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                } catch (CharacterCodingException x) {
-                    // Substitution is always enabled,
-                    // so this shouldn't happen
-                    throw new Error(x);
+                if (!isTrusted) {
+                    val = Arrays.copyOf(val, val.length);
                 }
-                return safeTrim(ba, bb.position(), cs, isTrusted);
+                int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba)
+                                              : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba);
+                if (blen != -1) {
+                    return safeTrim(ba, blen, isTrusted);
+                }
             }
+            char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val)
+                                           : StringUTF16.toChars(val);
+            ce.reset();
+            ByteBuffer bb = ByteBuffer.wrap(ba);
+            CharBuffer cb = CharBuffer.wrap(ca, 0, len);
+            try {
+                CoderResult cr = ce.encode(cb, bb, true);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+                cr = ce.flush(bb);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+            } catch (CharacterCodingException x) {
+                // Substitution is always enabled,
+                // so this shouldn't happen
+                throw new Error(x);
+            }
+            return safeTrim(ba, bb.position(), isTrusted);
         }
     }
 
-    static byte[] encode(String charsetName, char[] ca, int off, int len)
+    @HotSpotIntrinsicCandidate
+    private static int implEncodeISOArray(byte[] sa, int sp,
+                                          byte[] da, int dp, int len) {
+        int i = 0;
+        for (; i < len; i++) {
+            char c = StringUTF16.getChar(sa, sp++);
+            if (c > '\u00FF')
+                break;
+            da[dp++] = (byte)c;
+        }
+        return i;
+    }
+
+    static byte[] encode8859_1(byte coder, byte[] val) {
+        if (coder == LATIN1) {
+            return Arrays.copyOf(val, val.length);
+        }
+        int len = val.length >> 1;
+        byte[] dst = new byte[len];
+        int dp = 0;
+        int sp = 0;
+        int sl = len;
+        while (sp < sl) {
+            int ret = implEncodeISOArray(val, sp, dst, dp, len);
+            sp = sp + ret;
+            dp = dp + ret;
+            if (ret != len) {
+                char c = StringUTF16.getChar(val, sp++);
+                if (Character.isHighSurrogate(c) && sp < sl &&
+                    Character.isLowSurrogate(StringUTF16.getChar(val, sp))) {
+                    sp++;
+                }
+                dst[dp++] = '?';
+                len = sl - sp;
+            }
+        }
+        if (dp == dst.length) {
+            return dst;
+        }
+        return Arrays.copyOf(dst, dp);
+    }
+
+    static byte[] encodeASCII(byte coder, byte[] val) {
+        if (coder == LATIN1) {
+            byte[] dst = new byte[val.length];
+            for (int i = 0; i < val.length; i++) {
+                if (val[i] < 0) {
+                    dst[i] = '?';
+                } else {
+                    dst[i] = val[i];
+                }
+            }
+            return dst;
+        }
+        int len = val.length >> 1;
+        byte[] dst = new byte[len];
+        int dp = 0;
+        for (int i = 0; i < len; i++) {
+            char c = StringUTF16.getChar(val, i);
+            if (c < 0x80) {
+                dst[dp++] = (byte)c;
+                continue;
+            }
+            if (Character.isHighSurrogate(c) && i + 1 < len &&
+                Character.isLowSurrogate(StringUTF16.getChar(val, i + 1))) {
+                i++;
+            }
+            dst[dp++] = '?';
+        }
+        if (len == dp) {
+            return dst;
+        }
+        return Arrays.copyOf(dst, dp);
+    }
+
+   static byte[] encodeUTF8(byte coder, byte[] val) {
+        int dp = 0;
+        byte[] dst;
+        if (coder == LATIN1) {
+            dst = new byte[val.length << 1];
+            for (int sp = 0; sp < val.length; sp++) {
+                byte c = val[sp];
+                if (c < 0) {
+                    dst[dp++] = (byte)(0xc0 | ((c & 0xff) >> 6));
+                    dst[dp++] = (byte)(0x80 | (c & 0x3f));
+                } else {
+                    dst[dp++] = c;
+                }
+            }
+        } else {
+            int sp = 0;
+            int sl = val.length >> 1;
+            dst = new byte[sl * 3];
+            char c;
+            while (sp < sl && (c = StringUTF16.getChar(val, sp)) < '\u0080') {
+                // ascii fast loop;
+                dst[dp++] = (byte)c;
+                sp++;
+            }
+            while (sp < sl) {
+                c = StringUTF16.getChar(val, sp++);
+                if (c < 0x80) {
+                    dst[dp++] = (byte)c;
+                } else if (c < 0x800) {
+                    dst[dp++] = (byte)(0xc0 | (c >> 6));
+                    dst[dp++] = (byte)(0x80 | (c & 0x3f));
+                } else if (Character.isSurrogate(c)) {
+                    int uc = -1;
+                    char c2;
+                    if (Character.isHighSurrogate(c) && sp < sl &&
+                        Character.isLowSurrogate(c2 = StringUTF16.getChar(val, sp))) {
+                        uc = Character.toCodePoint(c, c2);
+                    }
+                    if (uc < 0) {
+                        dst[dp++] = '?';
+                    } else {
+                        dst[dp++] = (byte)(0xf0 | ((uc >> 18)));
+                        dst[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f));
+                        dst[dp++] = (byte)(0x80 | ((uc >>  6) & 0x3f));
+                        dst[dp++] = (byte)(0x80 | (uc & 0x3f));
+                        sp++;  // 2 chars
+                    }
+                } else {
+                    // 3 bytes, 16 bits
+                    dst[dp++] = (byte)(0xe0 | ((c >> 12)));
+                    dst[dp++] = (byte)(0x80 | ((c >>  6) & 0x3f));
+                    dst[dp++] = (byte)(0x80 | (c & 0x3f));
+                }
+            }
+        }
+        if (dp == dst.length) {
+            return dst;
+        }
+        return Arrays.copyOf(dst, dp);
+    }
+
+    static byte[] encode(String charsetName, byte coder, byte[] val)
         throws UnsupportedEncodingException
     {
         StringEncoder se = deref(encoder);
@@ -334,62 +570,88 @@
             se = null;
             try {
                 Charset cs = lookupCharset(csn);
-                if (cs != null)
+                if (cs != null) {
+                    if (cs == UTF_8) {
+                        return encodeUTF8(coder, val);
+                    } else if (cs == ISO_8859_1) {
+                        return encode8859_1(coder, val);
+                    } else if (cs == US_ASCII) {
+                        return encodeASCII(coder, val);
+                    }
                     se = new StringEncoder(cs, csn);
+                }
             } catch (IllegalCharsetNameException x) {}
-            if (se == null)
+            if (se == null) {
                 throw new UnsupportedEncodingException (csn);
+            }
             set(encoder, se);
         }
-        return se.encode(ca, off, len);
+        return se.encode(coder, val);
     }
 
-    static byte[] encode(Charset cs, char[] ca, int off, int len) {
+    static byte[] encode(Charset cs, byte coder, byte[] val) {
+        if (cs == UTF_8) {
+            return encodeUTF8(coder, val);
+        } else if (cs == ISO_8859_1) {
+            return encode8859_1(coder, val);
+        } else if (cs == US_ASCII) {
+            return encodeASCII(coder, val);
+        }
         CharsetEncoder ce = cs.newEncoder();
+        // fastpath for ascii compatible
+        if (coder == LATIN1 && (((ce instanceof ArrayEncoder) &&
+                                 ((ArrayEncoder)ce).isASCIICompatible() &&
+                                 !hasNegatives(val, 0, val.length)))) {
+            return Arrays.copyOf(val, val.length);
+        }
+        int len = val.length >> coder;  // assume LATIN1=0/UTF16=1;
         int en = scale(len, ce.maxBytesPerChar());
         byte[] ba = new byte[en];
-        if (len == 0)
+        if (len == 0) {
             return ba;
-        boolean isTrusted = false;
-        if (System.getSecurityManager() != null) {
-            if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
-                ca =  Arrays.copyOfRange(ca, off, off + len);
-                off = 0;
-            }
         }
+        boolean isTrusted = System.getSecurityManager() == null ||
+                            cs.getClass().getClassLoader0() == null;
         ce.onMalformedInput(CodingErrorAction.REPLACE)
           .onUnmappableCharacter(CodingErrorAction.REPLACE)
           .reset();
         if (ce instanceof ArrayEncoder) {
-            int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
-            return safeTrim(ba, blen, cs, isTrusted);
-        } else {
-            ByteBuffer bb = ByteBuffer.wrap(ba);
-            CharBuffer cb = CharBuffer.wrap(ca, off, len);
-            try {
-                CoderResult cr = ce.encode(cb, bb, true);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-                cr = ce.flush(bb);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-            } catch (CharacterCodingException x) {
-                throw new Error(x);
+            if (!isTrusted) {
+                val = Arrays.copyOf(val, val.length);
             }
-            return safeTrim(ba, bb.position(), cs, isTrusted);
+            int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba)
+                                          : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba);
+            if (blen != -1) {
+                return safeTrim(ba, blen, isTrusted);
+            }
         }
+        char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val)
+                                       : StringUTF16.toChars(val);
+        ByteBuffer bb = ByteBuffer.wrap(ba);
+        CharBuffer cb = CharBuffer.wrap(ca, 0, len);
+        try {
+            CoderResult cr = ce.encode(cb, bb, true);
+            if (!cr.isUnderflow())
+                cr.throwException();
+            cr = ce.flush(bb);
+            if (!cr.isUnderflow())
+                cr.throwException();
+        } catch (CharacterCodingException x) {
+            throw new Error(x);
+        }
+        return safeTrim(ba, bb.position(), isTrusted);
     }
 
-    static byte[] encode(char[] ca, int off, int len) {
+    static byte[] encode(byte coder, byte[] val) {
         String csn = Charset.defaultCharset().name();
         try {
             // use charset name encode() variant which provides caching.
-            return encode(csn, ca, off, len);
+            return encode(csn, coder, val);
         } catch (UnsupportedEncodingException x) {
             warnUnsupportedCharset(csn);
         }
         try {
-            return encode("ISO-8859-1", ca, off, len);
+            return encode("ISO-8859-1", coder, val);
         } catch (UnsupportedEncodingException x) {
             // If this code is hit during VM initialization, MessageUtils is
             // the only way we will be able to get any kind of error message.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/StringDecoderUTF8.java	Mon Nov 09 20:37:05 2015 -0800
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.Character.isSurrogate;
+import static java.lang.Character.highSurrogate;
+import static java.lang.Character.lowSurrogate;
+import static java.lang.Character.isSupplementaryCodePoint;
+import static java.lang.StringUTF16.putChar;
+
+class StringDecoderUTF8 extends StringCoding.StringDecoder {
+
+    StringDecoderUTF8(Charset cs, String rcn) {
+        super(cs, rcn);
+    }
+
+    private static boolean isNotContinuation(int b) {
+        return (b & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed3(int b1, int b2, int b3) {
+        return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) ||
+               (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed3_2(int b1, int b2) {
+        return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) ||
+               (b2 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed4(int b2, int b3, int b4) {
+        return (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 ||
+               (b4 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed4_2(int b1, int b2) {
+        return (b1 == 0xf0 && (b2  < 0x90 || b2 > 0xbf)) ||
+               (b1 == 0xf4 && (b2 & 0xf0) != 0x80) ||
+               (b2 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed4_3(int b3) {
+        return (b3 & 0xc0) != 0x80;
+    }
+
+    // for nb == 3/4
+    private static int malformedN(byte[] src, int sp, int nb) {
+        if (nb == 3) {
+            int b1 = src[sp++];
+            int b2 = src[sp++];    // no need to lookup b3
+            return ((b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) ||
+                    isNotContinuation(b2)) ? 1 : 2;
+        } else if (nb == 4) { // we don't care the speed here
+            int b1 = src[sp++] & 0xff;
+            int b2 = src[sp++] & 0xff;
+            if (b1 > 0xf4 ||
+                (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) ||
+                (b1 == 0xf4 && (b2 & 0xf0) != 0x80) ||
+                isNotContinuation(b2))
+                return 1;
+            if (isNotContinuation(src[sp++]))
+                return 2;
+            return 3;
+        }
+        assert false;
+        return -1;
+    }
+
+    private static char repl = '\ufffd';
+
+    StringCoding.Result decode(byte[] src, int sp, int len) {
+        return decode(src, sp, len, result);
+    }
+
+    static StringCoding.Result decode(byte[] src, int sp, int len,
+                                      StringCoding.Result ret) {
+        int sl = sp + len;
+        byte[] dst = new byte[len];
+        int dp = 0;
+        if (COMPACT_STRINGS) {   // Latin1 only loop
+            while (sp < sl) {
+                int b1 = src[sp];
+                if (b1 >= 0) {
+                    dst[dp++] = (byte)b1;
+                    sp++;
+                    continue;
+                }
+                if ((b1 == (byte)0xc2 || b1 == (byte)0xc3) &&
+                    sp + 1 < sl) {
+                    int b2 = src[sp + 1];
+                    if (!isNotContinuation(b2)) {
+                        dst[dp++] = (byte)(((b1 << 6) ^ b2)^
+                                           (((byte) 0xC0 << 6) ^
+                                           ((byte) 0x80 << 0)));
+                        sp += 2;
+                        continue;
+                    }
+                }
+                // anything not a latin1, including the repl
+                // we have to go with the utf16
+                break;
+            }
+            if (sp == sl) {
+                if (dp != dst.length) {
+                    dst = Arrays.copyOf(dst, dp);
+                }
+                return ret.with(dst, LATIN1);
+            }
+        }
+        if (dp == 0) {
+            dst = new byte[len << 1];
+        } else {
+            byte[] buf = new byte[len << 1];
+            StringLatin1.inflate(dst, 0, buf, 0, dp);
+            dst = buf;
+        }
+        while (sp < sl) {
+            int b1 = src[sp++];
+            if (b1 >= 0) {
+                putChar(dst, dp++, (char) b1);
+            } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) {
+                if (sp < sl) {
+                    int b2 = src[sp++];
+                    if (isNotContinuation(b2)) {
+                        putChar(dst, dp++, repl);
+                        sp--;
+                    } else {
+                        putChar(dst, dp++, (char)(((b1 << 6) ^ b2)^
+                                                  (((byte) 0xC0 << 6) ^
+                                                  ((byte) 0x80 << 0))));
+                    }
+                    continue;
+                }
+                putChar(dst, dp++, repl);
+                break;
+            } else if ((b1 >> 4) == -2) {
+                if (sp + 1 < sl) {
+                    int b2 = src[sp++];
+                    int b3 = src[sp++];
+                    if (isMalformed3(b1, b2, b3)) {
+                        putChar(dst, dp++, repl);
+                        sp -= 3;
+                        sp += malformedN(src, sp, 3);
+                    } else {
+                        char c = (char)((b1 << 12) ^
+                                        (b2 <<  6) ^
+                                        (b3 ^
+                                         (((byte) 0xE0 << 12) ^
+                                         ((byte) 0x80 <<  6) ^
+                                         ((byte) 0x80 <<  0))));
+                        putChar(dst, dp++, isSurrogate(c) ?  repl : c);
+                    }
+                    continue;
+                }
+                if (sp  < sl && isMalformed3_2(b1, src[sp])) {
+                    putChar(dst, dp++, repl);
+                    continue;
+                }
+                putChar(dst, dp++, repl);
+                break;
+            } else if ((b1 >> 3) == -2) {
+                if (sp + 2 < sl) {
+                    int b2 = src[sp++];
+                    int b3 = src[sp++];
+                    int b4 = src[sp++];
+                    int uc = ((b1 << 18) ^
+                              (b2 << 12) ^
+                              (b3 <<  6) ^
+                              (b4 ^
+                               (((byte) 0xF0 << 18) ^
+                               ((byte) 0x80 << 12) ^
+                               ((byte) 0x80 <<  6) ^
+                               ((byte) 0x80 <<  0))));
+                    if (isMalformed4(b2, b3, b4) ||
+                        !isSupplementaryCodePoint(uc)) { // shortest form check
+                        putChar(dst, dp++, repl);
+                        sp -= 4;
+                        sp += malformedN(src, sp, 4);
+                    } else {
+                        putChar(dst, dp++, highSurrogate(uc));
+                        putChar(dst, dp++, lowSurrogate(uc));
+                    }
+                    continue;
+                }
+                b1 &= 0xff;
+                if (b1 > 0xf4 ||
+                    sp  < sl && isMalformed4_2(b1, src[sp] & 0xff)) {
+                    putChar(dst, dp++, repl);
+                    continue;
+                }
+                sp++;
+                putChar(dst, dp++, repl);
+                if (sp  < sl && isMalformed4_3(src[sp])) {
+                    continue;
+                }
+                break;
+            } else {
+                putChar(dst, dp++, repl);
+            }
+        }
+        if (dp != len) {
+            dst = Arrays.copyOf(dst, dp << 1);
+        }
+        return ret.with(dst, UTF16);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/StringLatin1.java	Mon Nov 09 20:37:05 2015 -0800
@@ -0,0 +1,600 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Spliterator;
+import java.util.function.IntConsumer;
+import java.util.stream.IntStream;
+import jdk.internal.HotSpotIntrinsicCandidate;
+
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+import static java.lang.String.checkOffset;
+
+final class StringLatin1 {
+
+    public static char charAt(byte[] value, int index) {
+        if (index < 0 || index >= value.length) {
+            throw new StringIndexOutOfBoundsException(index);
+        }
+        return (char)(value[index] & 0xff);
+    }
+
+    public static boolean canEncode(int cp) {
+        return cp >>> 8 == 0;
+    }
+
+    public static int length(byte[] value) {
+        return value.length;
+    }
+
+    public static int codePointAt(byte[] value, int index, int end) {
+        return value[index] & 0xff;
+    }
+
+    public static int codePointBefore(byte[] value, int index) {
+        return value[index - 1] & 0xff;
+    }
+
+    public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
+        return endIndex - beginIndex;
+    }
+
+    public static char[] toChars(byte[] value) {
+        char[] dst = new char[value.length];
+        inflate(value, 0, dst, 0, value.length);
+        return dst;
+    }
+
+    public static byte[] inflate(byte[] value, int off, int len) {
+        byte[] ret = StringUTF16.newBytesFor(len);
+        inflate(value, off, ret, 0, len);
+        return ret;
+    }
+
+    public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+    }
+
+    public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
+        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static boolean equals(byte[] value, byte[] other) {
+        if (value.length == other.length) {
+            for (int i = 0; i < value.length; i++) {
+                if (value[i] != other[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareTo(byte[] value, byte[] other) {
+        int len1 = value.length;
+        int len2 = other.length;
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            if (value[k] != other[k]) {
+                return getChar(value, k) - getChar(other, k);
+            }
+        }
+        return len1 - len2;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareToUTF16(byte[] value, byte[] other) {
+        int len1 = length(value);
+        int len2 = StringUTF16.length(other);
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            char c1 = getChar(value, k);
+            char c2 = StringUTF16.getChar(other, k);
+            if (c1 != c2) {
+                return c1 - c2;
+            }
+        }
+        return len1 - len2;
+    }
+
+    public static int hashCode(byte[] value) {
+        int h = 0;
+        for (byte v : value) {
+            h = 31 * h + (v & 0xff);
+        }
+        return h;
+    }
+
+    public static int indexOf(byte[] value, int ch, int fromIndex) {
+        if (!canEncode(ch)) {
+            return -1;
+        }
+        int max = value.length;
+        if (fromIndex < 0) {
+            fromIndex = 0;
+        } else if (fromIndex >= max) {
+            // Note: fromIndex might be near -1>>>1.
+            return -1;
+        }
+        byte c = (byte)ch;
+        for (int i = fromIndex; i < max; i++) {
+            if (value[i] == c) {
+               return i;
+            }
+        }
+        return -1;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, byte[] str) {
+        if (str.length == 0) {
+            return 0;
+        }
+        if (value.length == 0) {
+            return -1;
+        }
+        return indexOf(value, value.length, str, str.length, 0);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
+        byte first = str[0];
+        int max = (valueCount - strCount);
+        for (int i = fromIndex; i <= max; i++) {
+            // Look for first character.
+            if (value[i] != first) {
+                while (++i <= max && value[i] != first);
+            }
+            // Found first character, now look at the rest of value
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + strCount - 1;
+                for (int k = 1; j < end && value[j] == str[k]; j++, k++);
+                if (j == end) {
+                    // Found whole string.
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public static int lastIndexOf(byte[] src, int srcCount,
+                                  byte[] tgt, int tgtCount, int fromIndex) {
+        int min = tgtCount - 1;
+        int i = min + fromIndex;
+        int strLastIndex = tgtCount - 1;
+        char strLastChar = (char)(tgt[strLastIndex] & 0xff);
+
+  startSearchForLastChar:
+        while (true) {
+            while (i >= min && (src[i] & 0xff) != strLastChar) {
+                i--;
+            }
+            if (i < min) {
+                return -1;
+            }
+            int j = i - 1;
+            int start = j - strLastIndex;
+            int k = strLastIndex - 1;
+            while (j > start) {
+                if ((src[j--] & 0xff) != (tgt[k--] & 0xff)) {
+                    i--;
+                    continue startSearchForLastChar;
+                }
+            }
+            return start + 1;
+        }
+    }
+
+    public static int lastIndexOf(final byte[] value, int ch, int fromIndex) {
+        if (!canEncode(ch)) {
+            return -1;
+        }
+        int off  = Math.min(fromIndex, value.length - 1);
+        for (; off >= 0; off--) {
+            if (value[off] == (byte)ch) {
+                return off;
+            }
+        }
+        return -1;
+    }
+
+    public static String replace(byte[] value, char oldChar, char newChar) {
+        if (canEncode(oldChar)) {
+            int len = value.length;
+            int i = -1;
+            while (++i < len) {
+                if (value[i] == (byte)oldChar) {
+                    break;
+                }
+            }
+            if (i < len) {
+                if (canEncode(newChar)) {
+                    byte buf[] = new byte[len];
+                    for (int j = 0; j < i; j++) {    // TBD arraycopy?
+                        buf[j] = value[j];
+                    }
+                    while (i < len) {
+                        byte c = value[i];
+                        buf[i] = (c == (byte)oldChar) ? (byte)newChar : c;
+                        i++;
+                    }
+                    return new String(buf, LATIN1);
+                } else {
+                    byte[] buf = StringUTF16.newBytesFor(len);
+                    // inflate from latin1 to UTF16
+                    inflate(value, 0, buf, 0, i);
+                    while (i < len) {
+                        char c = (char)(value[i] & 0xff);
+                        StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c);
+                        i++;
+                    }
+                    return new String(buf, UTF16);
+                }
+            }
+        }
+        return null; // for string to return this;
+    }
+
+    // case insensitive
+    public static boolean regionMatchesCI(byte[] value, int toffset,
+                                          byte[] other, int ooffset, int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = (char)(value[toffset++] & 0xff);
+            char c2 = (char)(other[ooffset++] & 0xff);
+            if (c1 == c2) {
+                continue;
+            }
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean regionMatchesCI_UTF16(byte[] value, int toffset,
+                                                byte[] other, int ooffset, int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = (char)(value[toffset++] & 0xff);
+            char c2 = StringUTF16.getChar(other, ooffset++);
+            if (c1 == c2) {
+                continue;
+            }
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static String toLowerCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        final int len = value.length;
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++) {
+            int cp = value[first] & 0xff;
+            if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len)
+            return str;
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toLowerCaseEx(str, value, first, locale, true);
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
+                                                       // lowerCase characters.
+        for (int i = first; i < len; i++) {
+            int cp = value[i] & 0xff;
+            cp = Character.toLowerCase(cp);
+            if (!canEncode(cp)) {                      // not a latin1 character
+                return toLowerCaseEx(str, value, first, locale, false);
+            }
+            result[i] = (byte)cp;
+        }
+        return new String(result, LATIN1);
+    }
+
+    private static String toLowerCaseEx(String str, byte[] value,
+                                        int first, Locale locale, boolean localeDependent)
+    {
+        byte[] result = StringUTF16.newBytesFor(value.length);
+        int resultOffset = 0;
+        for (int i = 0; i < first; i++) {
+            StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
+        }
+        for (int i = first; i < value.length; i++) {
+            int srcChar = value[i] & 0xff;
+            int lowerChar;
+            char[] lowerCharArray;
+            if (localeDependent) {
+                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
+            } else {
+                lowerChar = Character.toLowerCase(srcChar);
+            }
+            if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
+                StringUTF16.putChar(result, resultOffset++, lowerChar);
+            } else {
+                if (lowerChar == Character.ERROR) {
+                    lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
+                } else {
+                    lowerCharArray = Character.toChars(lowerChar);
+                }
+                /* Grow result if needed */
+                int mapLen = lowerCharArray.length;
+                if (mapLen > 1) {
+                    byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                }
+                for (int x = 0; x < mapLen; ++x) {
+                    StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]);
+                }
+            }
+        }
+        return StringUTF16.newString(result, 0, resultOffset);
+    }
+
+    public static String toUpperCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        final int len = value.length;
+
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++ ) {
+            int cp = value[first] & 0xff;
+            if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len) {
+            return str;
+        }
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toUpperCaseEx(str, value, first, locale, true);
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
+                                                       // upperCase characters.
+        for (int i = first; i < len; i++) {
+            int cp = value[i] & 0xff;
+            cp = Character.toUpperCaseEx(cp);
+            if (!canEncode(cp)) {                      // not a latin1 character
+                return toUpperCaseEx(str, value, first, locale, false);
+            }
+            result[i] = (byte)cp;
+        }
+        return new String(result, LATIN1);
+    }
+
+    private static String toUpperCaseEx(String str, byte[] value,
+                                        int first, Locale locale, boolean localeDependent)
+    {
+        byte[] result = StringUTF16.newBytesFor(value.length);
+        int resultOffset = 0;
+        for (int i = 0; i < first; i++) {
+            StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
+        }
+        for (int i = first; i < value.length; i++) {
+            int srcChar = value[i] & 0xff;
+            int upperChar;
+            char[] upperCharArray;
+            if (localeDependent) {
+                upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
+            } else {
+                upperChar = Character.toUpperCaseEx(srcChar);
+            }
+            if (Character.isBmpCodePoint(upperChar)) {
+                StringUTF16.putChar(result, resultOffset++, upperChar);
+            } else {
+                if (upperChar == Character.ERROR) {
+                    if (localeDependent) {
+                        upperCharArray =
+                            ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
+                    } else {
+                        upperCharArray = Character.toUpperCaseCharArray(srcChar);
+                    }
+                } else {
+                    upperCharArray = Character.toChars(upperChar);
+                }
+                /* Grow result if needed */
+                int mapLen = upperCharArray.length;
+                if (mapLen > 1) {
+                    byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                }
+                for (int x = 0; x < mapLen; ++x) {
+                    StringUTF16.putChar(result, resultOffset++, upperCharArray[x]);
+                }
+            }
+        }
+        return StringUTF16.newString(result, 0, resultOffset);
+    }
+
+    public static String trim(byte[] value) {
+        int len = value.length;
+        int st = 0;
+        while ((st < len) && ((value[st] & 0xff) <= ' ')) {
+            st++;
+        }
+        while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) {
+            len--;
+        }
+        return ((st > 0) || (len < value.length)) ?
+            newString(value, st, len - st) : null;
+    }
+
+    public static void putChar(byte[] val, int index, int c) {
+        //assert (canEncode(c));
+        val[index] = (byte)(c);
+    }
+
+    public static char getChar(byte[] val, int index) {
+        return (char)(val[index] & 0xff);
+    }
+
+    public static byte[] toBytes(int[] val, int off, int len) {
+        byte[] ret = new byte[len];
+        for (int i = 0; i < len; i++) {
+            int cp = val[off++];
+            if (!canEncode(cp)) {
+                return null;
+            }
+            ret[i] = (byte)cp;
+        }
+        return ret;
+    }
+
+    public static byte[] toBytes(char c) {
+        return new byte[] { (byte)c };
+    }
+
+    public static String newString(byte[] val, int index, int len) {
+        return new String(Arrays.copyOfRange(val, index, index + len),
+                          LATIN1);
+    }
+
+    public static void fillNull(byte[] val, int index, int end) {
+        Arrays.fill(val, index, end, (byte)0);
+    }
+
+    // inflatedCopy byte[] -> char[]
+    @HotSpotIntrinsicCandidate
+    private static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            dst[dstOff++] = (char)(src[srcOff++] & 0xff);
+        }
+    }
+
+    // inflatedCopy byte[] -> byte[]
+    @HotSpotIntrinsicCandidate
+    public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            StringUTF16.putChar(dst, dstOff++, src[srcOff++] & 0xff);
+        }
+    }
+
+    static class CharsSpliterator implements Spliterator.OfInt {
+        private final byte[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int cs;
+
+        CharsSpliterator(byte[] array, int acs) {
+            this(array, 0, array.length, acs);
+        }
+
+        CharsSpliterator(byte[] array, int origin, int fence, int acs) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
+                      | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public OfInt trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new CharsSpliterator(array, lo, index = mid, cs);
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            byte[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if ((a = array).length >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept(a[i] & 0xff); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                action.accept(array[index++] & 0xff);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return cs;
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////
+
+    public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        checkOffset(srcEnd, val.length);
+        getChars(val, srcBegin, srcEnd, dst, dstBegin);
+    }
+
+    public static void inflateSB(byte[] val, byte[] dst, int dstOff, int count) {
+        checkOffset(count, val.length);
+        checkOffset(dstOff + count, dst.length >> 1);  // dst is utf16
+        inflate(val, 0, dst, dstOff, count);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/StringUTF16.java	Mon Nov 09 20:37:05 2015 -0800
@@ -0,0 +1,971 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Spliterator;
+import java.util.function.IntConsumer;
+import jdk.internal.HotSpotIntrinsicCandidate;
+
+import static java.lang.String.UTF16;
+import static java.lang.String.LATIN1;
+import static java.lang.String.checkIndex;
+import static java.lang.String.checkOffset;
+
+final class StringUTF16 {
+
+    public static byte[] newBytesFor(int len) {
+        if (len < 0) {
+            throw new NegativeArraySizeException();
+        }
+        if (len > MAX_LENGTH) {
+            throw new OutOfMemoryError("UTF16 String size is " + len +
+                                       ", should be less than " + MAX_LENGTH);
+        }
+        return new byte[len << 1];
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static void putChar(byte[] val, int index, int c) {
+        index <<= 1;
+        val[index++] = (byte)(c >> HI_BYTE_SHIFT);
+        val[index]   = (byte)(c >> LO_BYTE_SHIFT);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static char getChar(byte[] val, int index) {
+        index <<= 1;
+        return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
+                      ((val[index]   & 0xff) << LO_BYTE_SHIFT));
+    }
+
+    public static char charAt(byte[] value, int index) {
+        if (index < 0 || index >= value.length >> 1) {
+            throw new StringIndexOutOfBoundsException(index);
+        }
+        return getChar(value, index);
+    }
+
+    public static int length(byte[] value) {
+        return value.length >> 1;
+    }
+
+    public static int codePointAt(byte[] value, int index, int end) {
+        char c1 = getChar(value, index);
+        if (Character.isHighSurrogate(c1) && ++index < end) {
+            char c2 = getChar(value, index);
+            if (Character.isLowSurrogate(c2)) {
+               return Character.toCodePoint(c1, c2);
+            }
+        }
+        return c1;
+    }
+
+    public static int codePointBefore(byte[] value, int index) {
+        char c2 = getChar(value, --index);
+        if (Character.isLowSurrogate(c2) && index > 0) {
+            char c1 = getChar(value, --index);
+            if (Character.isHighSurrogate(c1)) {
+               return Character.toCodePoint(c1, c2);
+            }
+        }
+        return c2;
+    }
+
+    public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
+        int count = endIndex - beginIndex;
+        for (int i = beginIndex; i < endIndex; ) {
+            if (Character.isHighSurrogate(getChar(value, i++)) &&
+                i < endIndex &&
+                Character.isLowSurrogate(getChar(value, i))) {
+                count--;
+                i++;
+            }
+        }
+        return count;
+    }
+
+    public static char[] toChars(byte[] value) {
+        char[] dst = new char[value.length >> 1];
+        getChars(value, 0, dst.length, dst, 0);
+        return dst;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static byte[] toBytes(char[] value, int off, int len) {
+        byte[] val = newBytesFor(len);
+        for (int i = 0; i < len; i++) {
+            putChar(val, i, value[off++]);
+        }
+        return val;
+    }
+
+    public static byte[] compress(char[] val, int off, int len) {
+        byte[] ret = new byte[len];
+        if (compress(val, off, ret, 0, len) == len) {
+            return ret;
+        }
+        return null;
+    }
+
+    public static byte[] compress(byte[] val, int off, int len) {
+        byte[] ret = new byte[len];
+        if (compress(val, off, ret, 0, len) == len) {
+            return ret;
+        }
+        return null;
+    }
+
+    // compressedCopy char[] -> byte[]
+    @HotSpotIntrinsicCandidate
+    private static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            int c = src[srcOff++];
+            if (c >>> 8 != 0) {
+                return 0;
+            }
+            dst[dstOff++] = (byte)c;
+        }
+        return len;
+    }
+
+    // compressedCopy byte[] -> byte[]
+    @HotSpotIntrinsicCandidate
+    public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            int c = getChar(src, srcOff++);
+            if (c >>> 8 != 0) {
+                return 0;
+            }
+            dst[dstOff++] = (byte)c;
+        }
+        return len;
+    }
+
+    public static byte[] toBytes(int[] val, int index, int len) {
+        final int end = index + len;
+        // Pass 1: Compute precise size of char[]
+        int n = len;
+        for (int i = index; i < end; i++) {
+            int cp = val[i];
+            if (Character.isBmpCodePoint(cp))
+                continue;
+            else if (Character.isValidCodePoint(cp))
+                n++;
+            else throw new IllegalArgumentException(Integer.toString(cp));
+        }
+        // Pass 2: Allocate and fill in <high, low> pair
+        byte[] buf = newBytesFor(n);
+        for (int i = index, j = 0; i < end; i++, j++) {
+            int cp = val[i];
+            if (Character.isBmpCodePoint(cp)) {
+                putChar(buf, j, cp);
+            } else {
+                putChar(buf, j++, Character.highSurrogate(cp));
+                putChar(buf, j, Character.lowSurrogate(cp));
+            }
+        }
+        return buf;
+    }
+
+    public static byte[] toBytes(char c) {
+        byte[] result = new byte[2];
+        putChar(result, 0, c);
+        return result;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        for (int i = srcBegin; i < srcEnd; i++) {
+            dst[dstBegin++] = getChar(value, i);
+        }
+    }
+
+    /* @see java.lang.String.getBytes(int, int, byte[], int) */
+    public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
+        srcBegin <<= 1;
+        srcEnd <<= 1;
+        for (int i = srcBegin + (1 >> LO_BYTE_SHIFT); i < srcEnd; i += 2) {
+            dst[dstBegin++] = value[i];
+        }
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static boolean equals(byte[] value, byte[] other) {
+        if (value.length == other.length) {
+            int len = value.length >> 1;
+            for (int i = 0; i < len; i++) {
+                if (getChar(value, i) != getChar(other, i)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareTo(byte[] value, byte[] other) {
+        int len1 = length(value);
+        int len2 = length(other);
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            char c1 = getChar(value, k);
+            char c2 = getChar(other, k);
+            if (c1 != c2) {
+                return c1 - c2;
+            }
+        }
+        return len1 - len2;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareToLatin1(byte[] value, byte[] other) {
+        int len1 = length(value);
+        int len2 = StringLatin1.length(other);
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            char c1 = getChar(value, k);
+            char c2 = StringLatin1.getChar(other, k);
+            if (c1 != c2) {
+                return c1 - c2;
+            }
+        }
+        return len1 - len2;
+    }
+
+    public static int hashCode(byte[] value) {
+        int h = 0;
+        int length = value.length >> 1;
+        for (int i = 0; i < length; i++) {
+            h = 31 * h + getChar(value, i);
+        }
+        return h;
+    }
+
+    public static int indexOf(byte[] value, int ch, int fromIndex) {
+        int max = value.length >> 1;
+        if (fromIndex < 0) {
+            fromIndex = 0;
+        } else if (fromIndex >= max) {
+            // Note: fromIndex might be near -1>>>1.
+            return -1;
+        }
+        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+            // handle most cases here (ch is a BMP code point or a
+            // negative value (invalid code point))
+            return indexOfChar(value, ch, fromIndex, max);
+        } else {
+            return indexOfSupplementary(value, ch, fromIndex, max);
+        }
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, byte[] str) {
+        if (str.length == 0) {
+            return 0;
+        }
+        if (value.length == 0) {
+            return -1;
+        }
+        return indexOf(value, length(value), str, length(str), 0);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
+        char first = getChar(str, 0);
+        int max = (valueCount - strCount);
+        for (int i = fromIndex; i <= max; i++) {
+            // Look for first character.
+            if (getChar(value, i) != first) {
+                while (++i <= max && getChar(value, i) != first);
+            }
+            // Found first character, now look at the rest of value
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + strCount - 1;
+                for (int k = 1; j < end && getChar(value, j) == getChar(str, k); j++, k++);
+                if (j == end) {
+                    // Found whole string.
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Handles indexOf Latin1 substring in UTF16 string.
+     */
+    @HotSpotIntrinsicCandidate
+    public static int indexOfLatin1(byte[] value, byte[] str) {
+        if (str.length == 0) {
+            return 0;
+        }
+        if (value.length == 0) {
+            return -1;
+        }
+        return indexOfLatin1(value, length(value), str, str.length, 0);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
+        char first = (char)(tgt[0] & 0xff);
+        int max = (srcCount - tgtCount);
+        for (int i = fromIndex; i <= max; i++) {
+            // Look for first character.
+            if (getChar(src, i) != first) {
+                while (++i <= max && getChar(src, i) != first);
+            }
+            // Found first character, now look at the rest of v2
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + tgtCount - 1;
+                for (int k = 1;
+                     j < end && getChar(src, j) == (tgt[k] & 0xff);
+                     j++, k++);
+                if (j == end) {
+                    // Found whole string.
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    @HotSpotIntrinsicCandidate
+    private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) {
+        for (int i = fromIndex; i < max; i++) {
+            if (getChar(value, i) == ch) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Handles (rare) calls of indexOf with a supplementary character.
+     */
+    private static int indexOfSupplementary(byte[] value, int ch, int fromIndex, int max) {
+        if (Character.isValidCodePoint(ch)) {
+            final char hi = Character.highSurrogate(ch);
+            final char lo = Character.lowSurrogate(ch);
+            for (int i = fromIndex; i < max - 1; i++) {
+                if (getChar(value, i) == hi && getChar(value, i + 1 ) == lo) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public static int lastIndexOf(byte[] src, int srcCount,
+                                  byte[] tgt, int tgtCount, int fromIndex) {
+        int min = tgtCount - 1;
+        int i = min + fromIndex;
+        int strLastIndex = tgtCount - 1;
+        char strLastChar = getChar(tgt, strLastIndex);
+
+    startSearchForLastChar:
+        while (true) {
+            while (i >= min && getChar(src, i) != strLastChar) {
+                i--;
+            }
+            if (i < min) {
+                return -1;
+            }
+            int j = i - 1;
+            int start = j - strLastIndex;
+            int k = strLastIndex - 1;
+            while (j > start) {
+                if (getChar(src, j--) != getChar(tgt, k--)) {
+                    i--;
+                    continue startSearchForLastChar;
+                }
+            }
+            return start + 1;
+        }
+    }
+
+    public static int lastIndexOf(byte[] value, int ch, int fromIndex) {
+        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+            // handle most cases here (ch is a BMP code point or a
+            // negative value (invalid code point))
+            int i = Math.min(fromIndex, (value.length >> 1) - 1);
+            for (; i >= 0; i--) {
+                if (getChar(value, i) == ch) {
+                    return i;
+                }
+            }
+            return -1;
+        } else {
+            return lastIndexOfSupplementary(value, ch, fromIndex);
+        }
+    }
+
+    /**
+     * Handles (rare) calls of lastIndexOf with a supplementary character.
+     */
+    private static int lastIndexOfSupplementary(final byte[] value, int ch, int fromIndex) {
+        if (Character.isValidCodePoint(ch)) {
+            char hi = Character.highSurrogate(ch);
+            char lo = Character.lowSurrogate(ch);
+            int i = Math.min(fromIndex, (value.length >> 1) - 2);
+            for (; i >= 0; i--) {
+                if (getChar(value, i) == hi && getChar(value, i + 1) == lo) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public static String replace(byte[] value, char oldChar, char newChar) {
+        int len = value.length >> 1;
+        int i = -1;
+        while (++i < len) {
+            if (getChar(value, i) == oldChar) {
+                break;
+            }
+        }
+        if (i < len) {
+            byte buf[] = new byte[value.length];
+            for (int j = 0; j < i; j++) {
+                putChar(buf, j, getChar(value, j)); // TBD:arraycopy?
+            }
+            while (i < len) {
+                char c = getChar(value, i);
+                putChar(buf, i, c == oldChar ? newChar : c);
+                i++;
+           }
+           // Check if we should try to compress to latin1
+           if (String.COMPACT_STRINGS &&
+               !StringLatin1.canEncode(oldChar) &&
+               StringLatin1.canEncode(newChar)) {
+               byte[] val = compress(buf, 0, len);
+               if (val != null) {
+                   return new String(val, LATIN1);
+               }
+           }
+           return new String(buf, UTF16);
+        }
+        return null;
+    }
+
+    public static boolean regionMatchesCI(byte[] value, int toffset,
+                                          byte[] other, int ooffset, int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = getChar(value, toffset++);
+            char c2 = getChar(other, ooffset++);
+            if (c1 == c2) {
+                continue;
+            }
+            // try converting both characters to uppercase.
+            // If the results match, then the comparison scan should
+            // continue.
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            // Unfortunately, conversion to uppercase does not work properly
+            // for the Georgian alphabet, which has strange rules about case
+            // conversion.  So we need to make one last check before
+            // exiting.
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean regionMatchesCI_Latin1(byte[] value, int toffset,
+                                                 byte[] other, int ooffset,
+                                                 int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = getChar(value, toffset++);
+            char c2 = (char)(other[ooffset++] & 0xff);
+            if (c1 == c2) {
+                continue;
+            }
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static String toLowerCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        boolean hasSurr = false;
+        final int len = value.length >> 1;
+
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++) {
+            int cp = (int)getChar(value, first);
+            if (Character.isSurrogate((char)cp)) {
+                hasSurr = true;
+                break;
+            }
+            if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len)
+            return str;
+        byte[] result = new byte[value.length];
+        System.arraycopy(value, 0, result, 0, first << 1);  // Just copy the first few
+                                                            // lowerCase characters.
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toLowerCaseEx(str, value, result, first, locale, true);
+        }
+        if (hasSurr) {
+            return toLowerCaseEx(str, value, result, first, locale, false);
+        }
+        int bits = 0;
+        for (int i = first; i < len; i++) {
+            int cp = (int)getChar(value, i);
+            if (cp == '\u03A3' ||                       // GREEK CAPITAL LETTER SIGMA
+                Character.isSurrogate((char)cp)) {
+                return toLowerCaseEx(str, value, result, i, locale, false);
+            }
+            if (cp == '\u0130') {                       // LATIN CAPITAL LETTER I WITH DOT ABOVE
+                return toLowerCaseEx(str, value, result, i, locale, true);
+            }
+            cp = Character.toLowerCase(cp);
+            if (!Character.isBmpCodePoint(cp)) {
+                return toLowerCaseEx(str, value, result, i, locale, false);
+            }
+            bits |= cp;
+            putChar(result, i, cp);
+        }
+        if (bits >>> 8 != 0) {
+            return new String(result, UTF16);
+        } else {
+            return newString(result, 0, len);
+        }
+    }
+
+    private static String toLowerCaseEx(String str, byte[] value,
+                                        byte[] result, int first, Locale locale,
+                                        boolean localeDependent) {
+        int resultOffset = first;
+        int length = value.length >> 1;
+        int srcCount;
+        for (int i = first; i < length; i += srcCount) {
+            int srcChar = getChar(value, i);
+            int lowerChar;
+            char[] lowerCharArray;
+            srcCount = 1;
+            if (Character.isSurrogate((char)srcChar)) {
+                srcChar = codePointAt(value, i, length);
+                srcCount = Character.charCount(srcChar);
+            }
+            if (localeDependent ||
+                srcChar == '\u03A3' ||  // GREEK CAPITAL LETTER SIGMA
+                srcChar == '\u0130') {  // LATIN CAPITAL LETTER I WITH DOT ABOVE
+                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
+            } else {
+                lowerChar = Character.toLowerCase(srcChar);
+            }
+            if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
+                putChar(result, resultOffset++, lowerChar);
+            } else {
+                if (lowerChar == Character.ERROR) {
+                    lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
+                } else {
+                    lowerCharArray = Character.toChars(lowerChar);
+                }
+                /* Grow result if needed */
+                int mapLen = lowerCharArray.length;
+                if (mapLen > srcCount) {
+                    byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                }
+                for (int x = 0; x < mapLen; ++x) {
+                    putChar(result, resultOffset++, lowerCharArray[x]);
+                }
+            }
+        }
+        return newString(result, 0, resultOffset);
+    }
+
+    public static String toUpperCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        boolean hasSurr = false;
+        final int len = value.length >> 1;
+
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++) {
+            int cp = (int)getChar(value, first);
+            if (Character.isSurrogate((char)cp)) {
+                hasSurr = true;
+                break;
+            }
+            if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len) {
+            return str;
+        }
+        byte[] result = new byte[value.length];
+        System.arraycopy(value, 0, result, 0, first << 1); // Just copy the first few
+                                                           // upperCase characters.
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toUpperCaseEx(str, value, result, first, locale, true);
+        }
+        if (hasSurr) {
+            return toUpperCaseEx(str, value, result, first, locale, false);
+        }
+        int bits = 0;
+        for (int i = first; i < len; i++) {
+            int cp = (int)getChar(value, i);
+            if (Character.isSurrogate((char)cp)) {
+                return toUpperCaseEx(str, value, result, i, locale, false);
+            }
+            cp = Character.toUpperCaseEx(cp);
+            if (!Character.isBmpCodePoint(cp)) {    // Character.ERROR is not bmp
+                return toUpperCaseEx(str, value, result, i, locale, false);
+            }
+            bits |= cp;
+            putChar(result, i, cp);
+        }
+        if (bits >>> 8 != 0) {
+            return new String(result, UTF16);
+        } else {
+            return newString(result, 0, len);
+        }
+    }
+
+    private static String toUpperCaseEx(String str, byte[] value,
+                                        byte[] result, int first,
+                                        Locale locale, boolean localeDependent)
+    {
+        int resultOffset = first;
+        int length = value.length >> 1;
+        int srcCount;
+        for (int i = first; i < length; i += srcCount) {
+            int srcChar = getChar(value, i);
+            int upperChar;
+            char[] upperCharArray;
+            srcCount = 1;
+            if (Character.isSurrogate((char)srcChar)) {
+                srcChar = codePointAt(value, i, length);
+                srcCount = Character.charCount(srcChar);
+            }
+            if (localeDependent) {
+                upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
+            } else {
+                upperChar = Character.toUpperCaseEx(srcChar);
+            }
+            if (Character.isBmpCodePoint(upperChar)) {
+                putChar(result, resultOffset++, upperChar);
+            } else {
+                if (upperChar == Character.ERROR) {
+                    if (localeDependent) {
+                        upperCharArray =
+                            ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
+                    } else {
+                        upperCharArray = Character.toUpperCaseCharArray(srcChar);
+                    }
+                } else {
+                    upperCharArray = Character.toChars(upperChar);
+                }
+                /* Grow result if needed */
+                int mapLen = upperCharArray.length;
+                if (mapLen > srcCount) {
+                    byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                 }
+                 for (int x = 0; x < mapLen; ++x) {
+                    putChar(result, resultOffset++, upperCharArray[x]);
+                 }
+            }
+        }
+        return newString(result, 0, resultOffset);
+    }
+
+    public static String trim(byte[] value) {
+        int length = value.length >> 1;
+        int len = length;
+        int st = 0;
+        while (st < len && getChar(value, st) <= ' ') {
+            st++;
+        }
+        while (st < len && getChar(value, len - 1) <= ' ') {
+            len--;
+        }
+        return ((st > 0) || (len < length )) ?
+            new String(Arrays.copyOfRange(value, st << 1, len << 1), UTF16) :
+            null;
+    }
+
+    public static void putChars(byte[] val, int index, char[] str, int off, int end) {
+        while (off < end) {
+            putChar(val, index++, str[off++]);
+        }
+    }
+
+    public static String newString(byte[] val, int index, int len) {
+        if (String.COMPACT_STRINGS) {
+            byte[] buf = compress(val, index, len);
+            if (buf != null) {
+                return new String(buf, LATIN1);
+            }
+        }
+        int last = index + len;
+        return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16);
+    }
+
+    public static void fillNull(byte[] val, int index, int end) {
+        Arrays.fill(val, index << 1, end << 1, (byte)0);
+    }
+
+    static class CharsSpliterator implements Spliterator.OfInt {
+        private final byte[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int cs;
+
+        CharsSpliterator(byte[] array, int acs) {
+            this(array, 0, array.length >> 1, acs);
+        }
+
+        CharsSpliterator(byte[] array, int origin, int fence, int acs) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
+                      | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public OfInt trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new CharsSpliterator(array, lo, index = mid, cs);
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            byte[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if (((a = array).length >> 1) >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept(getChar(a, i)); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                action.accept(getChar(array, index++));
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return cs;
+        }
+    }
+
+    static class CodePointsSpliterator implements Spliterator.OfInt {
+        private final byte[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int cs;
+
+        CodePointsSpliterator(byte[] array, int acs) {
+            this(array, 0, array.length >> 1, acs);
+        }
+
+        CodePointsSpliterator(byte[] array, int origin, int fence, int acs) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.cs = acs | Spliterator.ORDERED;
+        }
+
+        @Override
+        public OfInt trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            if (lo >= mid)
+                return null;
+
+            int midOneLess;
+            // If the mid-point intersects a surrogate pair
+            if (Character.isLowSurrogate(getChar(array, mid)) &&
+                Character.isHighSurrogate(getChar(array, midOneLess = (mid -1)))) {
+                // If there is only one pair it cannot be split
+                if (lo >= midOneLess)
+                    return null;
+                // Shift the mid-point to align with the surrogate pair
+                return new CodePointsSpliterator(array, lo, index = midOneLess, cs);
+            }
+            return new CodePointsSpliterator(array, lo, index = mid, cs);
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            byte[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if (((a = array).length >> 1) >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do {
+                    i = advance(a, i, hi, action);
+                } while (i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                index = advance(array, index, fence, action);
+                return true;
+            }
+            return false;
+        }
+
+        // Advance one code point from the index, i, and return the next
+        // index to advance from
+        private static int advance(byte[] a, int i, int hi, IntConsumer action) {
+            char c1 = getChar(a, i++);
+            int cp = c1;
+            if (Character.isHighSurrogate(c1) && i < hi) {
+                char c2 = getChar(a, i);
+                if (Character.isLowSurrogate(c2)) {
+                    i++;
+                    cp = Character.toCodePoint(c1, c2);
+                }
+            }
+            action.accept(cp);
+            return i;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return cs;
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////
+
+    public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        checkOffset(srcEnd, val.length >> 1);
+        getChars(val, srcBegin, srcEnd, dst, dstBegin);
+    }
+
+    public static void putCharSB(byte[] val, int index, int c) {
+        checkIndex(index, val.length >> 1);
+        putChar(val, index, c);
+    }
+
+    public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) {
+        checkOffset(index + end - off, val.length >> 1);
+        putChars(val, index, ca, off, end);
+    }
+
+    public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) {
+        checkOffset(index + end - off, val.length >> 1);
+        for (int i = off; i < end; i++) {
+            putChar(val, index++, s.charAt(i));
+        }
+    }
+
+    public static int codePointAtSB(byte[] val, int index, int end) {
+        checkOffset(end, val.length >> 1);
+        return codePointAt(val, index, end);
+    }
+
+    public static int codePointBeforeSB(byte[] val, int index) {
+        checkOffset(index, val.length >> 1);
+        return codePointBefore(val, index);
+    }
+
+    public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
+        checkOffset(endIndex, val.length >> 1);
+        return codePointCount(val, beginIndex, endIndex);
+    }
+
+    public static String newStringSB(byte[] val, int index, int len) {
+        checkOffset(index + len, val.length >> 1);
+        return newString(val, index, len);
+    }
+
+    ////////////////////////////////////////////////////////////////
+
+    private static native boolean isBigEndian();
+
+    static final int HI_BYTE_SHIFT;
+    static final int LO_BYTE_SHIFT;
+    static {
+        if (isBigEndian()) {
+            HI_BYTE_SHIFT = 8;
+            LO_BYTE_SHIFT = 0;
+        } else {
+            HI_BYTE_SHIFT = 0;
+            LO_BYTE_SHIFT = 8;
+        }
+    }
+
+    static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
+}
--- a/src/java.base/share/classes/java/nio/Bits.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/nio/Bits.java	Mon Nov 09 20:37:05 2015 -0800
@@ -32,7 +32,7 @@
 import jdk.internal.misc.JavaNioAccess;
 import jdk.internal.misc.JavaLangRefAccess;
 import jdk.internal.misc.SharedSecrets;
-import sun.misc.Unsafe;
+import jdk.internal.misc.Unsafe;
 import sun.misc.VM;
 
 /**
--- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template	Mon Nov 09 20:37:05 2015 -0800
@@ -29,7 +29,7 @@
 
 import java.io.FileDescriptor;
 import sun.misc.Cleaner;
-import sun.misc.Unsafe;
+import jdk.internal.misc.Unsafe;
 import sun.misc.VM;
 import sun.nio.ch.DirectBuffer;
 
--- a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template	Mon Nov 09 20:37:05 2015 -0800
@@ -27,7 +27,7 @@
 
 package java.nio;
 
-import sun.misc.Unsafe;
+import jdk.internal.misc.Unsafe;
 
 /**
 #if[rw]
@@ -477,7 +477,7 @@
 #if[rw]
 
     public float getFloat() {
-        int x = unsafe.getIntUnaligned(hb, byteOffset(nextPutIndex(4)), bigEndian);
+        int x = unsafe.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
         return Float.intBitsToFloat(x);
     }
 
--- a/src/java.base/share/classes/java/util/Arrays.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/java/util/Arrays.java	Mon Nov 09 20:37:05 2015 -0800
@@ -2882,6 +2882,7 @@
      * @param a2 the other array to be tested for equality
      * @return {@code true} if the two arrays are equal
      */
+    @HotSpotIntrinsicCandidate
     public static boolean equals(byte[] a, byte[] a2) {
         if (a==a2)
             return true;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java	Mon Nov 09 20:37:05 2015 -0800
@@ -0,0 +1,1391 @@
+/*
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.misc;
+
+import java.lang.reflect.Field;
+import java.security.ProtectionDomain;
+
+import sun.reflect.CallerSensitive;
+import sun.reflect.Reflection;
+import sun.misc.VM;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
+
+
+/**
+ * A collection of methods for performing low-level, unsafe operations.
+ * Although the class and all methods are public, use of this class is
+ * limited because only trusted code can obtain instances of it.
+ *
+ * @author John R. Rose
+ * @see #getUnsafe
+ */
+
+public final class Unsafe {
+
+    private static native void registerNatives();
+    static {
+        registerNatives();
+        sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
+    }
+
+    private Unsafe() {}
+
+    private static final Unsafe theUnsafe = new Unsafe();
+
+    /**
+     * Provides the caller with the capability of performing unsafe
+     * operations.
+     *
+     * <p>The returned {@code Unsafe} object should be carefully guarded
+     * by the caller, since it can be used to read and write data at arbitrary
+     * memory addresses.  It must never be passed to untrusted code.
+     *
+     * <p>Most methods in this class are very low-level, and correspond to a
+     * small number of hardware instructions (on typical machines).  Compilers
+     * are encouraged to optimize these methods accordingly.
+     *
+     * <p>Here is a suggested idiom for using unsafe operations:
+     *
+     * <pre> {@code
+     * class MyTrustedClass {
+     *   private static final Unsafe unsafe = Unsafe.getUnsafe();
+     *   ...
+     *   private long myCountAddress = ...;
+     *   public int getCount() { return unsafe.getByte(myCountAddress); }
+     * }}</pre>
+     *
+     * (It may assist compilers to make the local variable {@code final}.)
+     *
+     * @throws  SecurityException  if a security manager exists and its
+     *          {@code checkPropertiesAccess} method doesn't allow
+     *          access to the system properties.
+     */
+    @CallerSensitive
+    public static Unsafe getUnsafe() {
+        Class<?> caller = Reflection.getCallerClass();
+        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
+            throw new SecurityException("Unsafe");
+        return theUnsafe;
+    }
+
+    /// peek and poke operations
+    /// (compilers should optimize these to memory ops)
+
+    // These work on object fields in the Java heap.
+    // They will not work on elements of packed arrays.
+
+    /**
+     * Fetches a value from a given Java variable.
+     * More specifically, fetches a field or array element within the given
+     * object {@code o} at the given offset, or (if {@code o} is null)
+     * from the memory address whose numerical value is the given offset.
+     * <p>
+     * The results are undefined unless one of the following cases is true:
+     * <ul>
+     * <li>The offset was obtained from {@link #objectFieldOffset} on
+     * the {@link java.lang.reflect.Field} of some Java field and the object
+     * referred to by {@code o} is of a class compatible with that
+     * field's class.
+     *
+     * <li>The offset and object reference {@code o} (either null or
+     * non-null) were both obtained via {@link #staticFieldOffset}
+     * and {@link #staticFieldBase} (respectively) from the
+     * reflective {@link Field} representation of some Java field.
+     *
+     * <li>The object referred to by {@code o} is an array, and the offset
+     * is an integer of the form {@code B+N*S}, where {@code N} is
+     * a valid index into the array, and {@code B} and {@code S} are
+     * the values obtained by {@link #arrayBaseOffset} and {@link
+     * #arrayIndexScale} (respectively) from the array's class.  The value
+     * referred to is the {@code N}<em>th</em> element of the array.
+     *
+     * </ul>
+     * <p>
+     * If one of the above cases is true, the call references a specific Java
+     * variable (field or array element).  However, the results are undefined
+     * if that variable is not in fact of the type returned by this method.
+     * <p>
+     * This method refers to a variable by means of two parameters, and so
+     * it provides (in effect) a <em>double-register</em> addressing mode
+     * for Java variables.  When the object reference is null, this method
+     * uses its offset as an absolute address.  This is similar in operation
+     * to methods such as {@link #getInt(long)}, which provide (in effect) a
+     * <em>single-register</em> addressing mode for non-Java variables.
+     * However, because Java variables may have a different layout in memory
+     * from non-Java variables, programmers should not assume that these
+     * two addressing modes are ever equivalent.  Also, programmers should
+     * remember that offsets from the double-register addressing mode cannot
+     * be portably confused with longs used in the single-register addressing
+     * mode.
+     *
+     * @param o Java heap object in which the variable resides, if any, else
+     *        null
+     * @param offset indication of where the variable resides in a Java heap
+     *        object, if any, else a memory address locating the variable
+     *        statically
+     * @return the value fetched from the indicated Java variable
+     * @throws RuntimeException No defined exceptions are thrown, not even
+     *         {@link NullPointerException}
+     */
+    @HotSpotIntrinsicCandidate
+    public native int getInt(Object o, long offset);
+
+    /**
+     * Stores a value into a given Java variable.
+     * <p>
+     * The first two parameters are interpreted exactly as with
+     * {@link #getInt(Object, long)} to refer to a specific
+     * Java variable (field or array element).  The given value
+     * is stored into that variable.
+     * <p>
+     * The variable must be of the same type as the method
+     * parameter {@code x}.
+     *
+     * @param o Java heap object in which the variable resides, if any, else
+     *        null
+     * @param offset indication of where the variable resides in a Java heap
+     *        object, if any, else a memory address locating the variable
+     *        statically
+     * @param x the value to store into the indicated Java variable
+     * @throws RuntimeException No defined exceptions are thrown, not even
+     *         {@link NullPointerException}
+     */
+    @HotSpotIntrinsicCandidate
+    public native void putInt(Object o, long offset, int x);
+
+    /**
+     * Fetches a reference value from a given Java variable.
+     * @see #getInt(Object, long)
+     */
+    @HotSpotIntrinsicCandidate
+    public native Object getObject(Object o, long offset);
+
+    /**
+     * Stores a reference value into a given Java variable.
+     * <p>
+     * Unless the reference {@code x} being stored is either null
+     * or matches the field type, the results are undefined.
+     * If the reference {@code o} is non-null, card marks or
+     * other store barriers for that object (if the VM requires them)
+     * are updated.
+     * @see #putInt(Object, long, int)
+     */
+    @HotSpotIntrinsicCandidate
+    public native void putObject(Object o, long offset, Object x);
+
+    /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public native boolean getBoolean(Object o, long offset);
+    /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
+    public native void    putBoolean(Object o, long offset, boolean x);
+    /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public native byte    getByte(Object o, long offset);
+    /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
+    public native void    putByte(Object o, long offset, byte x);
+    /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public native short   getShort(Object o, long offset);
+    /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
+    public native void    putShort(Object o, long offset, short x);
+    /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public native char    getChar(Object o, long offset);
+    /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
+    public native void    putChar(Object o, long offset, char x);
+    /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public native long    getLong(Object o, long offset);
+    /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
+    public native void    putLong(Object o, long offset, long x);
+    /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public native float   getFloat(Object o, long offset);
+    /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
+    public native void    putFloat(Object o, long offset, float x);
+    /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public native double  getDouble(Object o, long offset);
+    /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
+    public native void    putDouble(Object o, long offset, double x);
+
+    // These read VM internal data.
+
+    /**
+     * Fetches an uncompressed reference value from a given native variable
+     * ignoring the VM's compressed references mode.
+     *
+     * @param address a memory address locating the variable
+     * @return the value fetched from the indicated native variable
+     */
+    public native Object getUncompressedObject(long address);
+
+    /**
+     * Fetches the {@link java.lang.Class} Java mirror for the given native
+     * metaspace {@code Klass} pointer.
+     *
+     * @param metaspaceKlass a native metaspace {@code Klass} pointer
+     * @return the {@link java.lang.Class} Java mirror
+     */
+    public native Class<?> getJavaMirror(long metaspaceKlass);
+
+    /**
+     * Fetches a native metaspace {@code Klass} pointer for the given Java
+     * object.
+     *
+     * @param o Java heap object for which to fetch the class pointer
+     * @return a native metaspace {@code Klass} pointer
+     */
+    public native long getKlassPointer(Object o);
+
+    // These work on values in the C heap.
+
+    /**
+     * Fetches a value from a given memory address.  If the address is zero, or
+     * does not point into a block obtained from {@link #allocateMemory}, the
+     * results are undefined.
+     *
+     * @see #allocateMemory
+     */
+    @HotSpotIntrinsicCandidate
+    public native byte    getByte(long address);
+
+    /**
+     * Stores a value into a given memory address.  If the address is zero, or
+     * does not point into a block obtained from {@link #allocateMemory}, the
+     * results are undefined.
+     *
+     * @see #getByte(long)
+     */
+    @HotSpotIntrinsicCandidate
+    public native void    putByte(long address, byte x);
+
+    /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
+    public native short   getShort(long address);
+    /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
+    public native void    putShort(long address, short x);
+    /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
+    public native char    getChar(long address);
+    /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
+    public native void    putChar(long address, char x);
+    /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
+    public native int     getInt(long address);
+    /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
+    public native void    putInt(long address, int x);
+    /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
+    public native long    getLong(long address);
+    /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
+    public native void    putLong(long address, long x);
+    /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
+    public native float   getFloat(long address);
+    /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
+    public native void    putFloat(long address, float x);
+    /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
+    public native double  getDouble(long address);
+    /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
+    public native void    putDouble(long address, double x);
+
+    /**
+     * Fetches a native pointer from a given memory address.  If the address is
+     * zero, or does not point into a block obtained from {@link
+     * #allocateMemory}, the results are undefined.
+     *
+     * <p>If the native pointer is less than 64 bits wide, it is extended as
+     * an unsigned number to a Java long.  The pointer may be indexed by any
+     * given byte offset, simply by adding that offset (as a simple integer) to
+     * the long representing the pointer.  The number of bytes actually read
+     * from the target address may be determined by consulting {@link
+     * #addressSize}.
+     *
+     * @see #allocateMemory
+     */
+    @HotSpotIntrinsicCandidate
+    public native long getAddress(long address);
+
+    /**
+     * Stores a native pointer into a given memory address.  If the address is
+     * zero, or does not point into a block obtained from {@link
+     * #allocateMemory}, the results are undefined.
+     *
+     * <p>The number of bytes actually written at the target address may be
+     * determined by consulting {@link #addressSize}.
+     *
+     * @see #getAddress(long)
+     */
+    @HotSpotIntrinsicCandidate
+    public native void putAddress(long address, long x);
+
+    /// wrappers for malloc, realloc, free:
+
+    /**
+     * Allocates a new block of native memory, of the given size in bytes.  The
+     * contents of the memory are uninitialized; they will generally be
+     * garbage.  The resulting native pointer will never be zero, and will be
+     * aligned for all value types.  Dispose of this memory by calling {@link
+     * #freeMemory}, or resize it with {@link #reallocateMemory}.
+     *
+     * @throws IllegalArgumentException if the size is negative or too large
+     *         for the native size_t type
+     *
+     * @throws OutOfMemoryError if the allocation is refused by the system
+     *
+     * @see #getByte(long)
+     * @see #putByte(long, byte)
+     */
+    public native long allocateMemory(long bytes);
+
+    /**
+     * Resizes a new block of native memory, to the given size in bytes.  The
+     * contents of the new block past the size of the old block are
+     * uninitialized; they will generally be garbage.  The resulting native
+     * pointer will be zero if and only if the requested size is zero.  The
+     * resulting native pointer will be aligned for all value types.  Dispose
+     * of this memory by calling {@link #freeMemory}, or resize it with {@link
+     * #reallocateMemory}.  The address passed to this method may be null, in
+     * which case an allocation will be performed.
+     *
+     * @throws IllegalArgumentException if the size is negative or too large
+     *         for the native size_t type
+     *
+     * @throws OutOfMemoryError if the allocation is refused by the system
+     *
+     * @see #allocateMemory
+     */
+    public native long reallocateMemory(long address, long bytes);
+
+    /**
+     * Sets all bytes in a given block of memory to a fixed value
+     * (usually zero).
+     *
+     * <p>This method determines a block's base address by means of two parameters,
+     * and so it provides (in effect) a <em>double-register</em> addressing mode,
+     * as discussed in {@link #getInt(Object,long)}.  When the object reference is null,
+     * the offset supplies an absolute base address.
+     *
+     * <p>The stores are in coherent (atomic) units of a size determined
+     * by the address and length parameters.  If the effective address and
+     * length are all even modulo 8, the stores take place in 'long' units.
+     * If the effective address and length are (resp.) even modulo 4 or 2,
+     * the stores take place in units of 'int' or 'short'.
+     *
+     * @since 1.7
+     */
+    public native void setMemory(Object o, long offset, long bytes, byte value);
+
+    /**
+     * Sets all bytes in a given block of memory to a fixed value
+     * (usually zero).  This provides a <em>single-register</em> addressing mode,
+     * as discussed in {@link #getInt(Object,long)}.
+     *
+     * <p>Equivalent to {@code setMemory(null, address, bytes, value)}.
+     */
+    public void setMemory(long address, long bytes, byte value) {
+        setMemory(null, address, bytes, value);
+    }
+
+    /**
+     * Sets all bytes in a given block of memory to a copy of another
+     * block.
+     *
+     * <p>This method determines each block's base address by means of two parameters,
+     * and so it provides (in effect) a <em>double-register</em> addressing mode,
+     * as discussed in {@link #getInt(Object,long)}.  When the object reference is null,
+     * the offset supplies an absolute base address.
+     *
+     * <p>The transfers are in coherent (atomic) units of a size determined
+     * by the address and length parameters.  If the effective addresses and
+     * length are all even modulo 8, the transfer takes place in 'long' units.
+     * If the effective addresses and length are (resp.) even modulo 4 or 2,
+     * the transfer takes place in units of 'int' or 'short'.
+     *
+     * @since 1.7
+     */
+    @HotSpotIntrinsicCandidate
+    public native void copyMemory(Object srcBase, long srcOffset,
+                                  Object destBase, long destOffset,
+                                  long bytes);
+    /**
+     * Sets all bytes in a given block of memory to a copy of another
+     * block.  This provides a <em>single-register</em> addressing mode,
+     * as discussed in {@link #getInt(Object,long)}.
+     *
+     * Equivalent to {@code copyMemory(null, srcAddress, null, destAddress, bytes)}.
+     */
+    public void copyMemory(long srcAddress, long destAddress, long bytes) {
+        copyMemory(null, srcAddress, null, destAddress, bytes);
+    }
+
+    /**
+     * Disposes of a block of native memory, as obtained from {@link
+     * #allocateMemory} or {@link #reallocateMemory}.  The address passed to
+     * this method may be null, in which case no action is taken.
+     *
+     * @see #allocateMemory
+     */
+    public native void freeMemory(long address);
+
+    /// random queries
+
+    /**
+     * This constant differs from all results that will ever be returned from
+     * {@link #staticFieldOffset}, {@link #objectFieldOffset},
+     * or {@link #arrayBaseOffset}.
+     */
+    public static final int INVALID_FIELD_OFFSET   = -1;
+
+    /**
+     * Reports the location of a given field in the storage allocation of its
+     * class.  Do not expect to perform any sort of arithmetic on this offset;
+     * it is just a cookie which is passed to the unsafe heap memory accessors.
+     *
+     * <p>Any given field will always have the same offset and base, and no
+     * two distinct fields of the same class will ever have the same offset
+     * and base.
+     *
+     * <p>As of 1.4.1, offsets for fields are represented as long values,
+     * although the Sun JVM does not use the most significant 32 bits.
+     * However, JVM implementations which store static fields at absolute
+     * addresses can use long offsets and null base pointers to express
+     * the field locations in a form usable by {@link #getInt(Object,long)}.
+     * Therefore, code which will be ported to such JVMs on 64-bit platforms
+     * must preserve all bits of static field offsets.
+     * @see #getInt(Object, long)
+     */
+    public native long objectFieldOffset(Field f);
+
+    /**
+     * Reports the location of a given static field, in conjunction with {@link
+     * #staticFieldBase}.
+     * <p>Do not expect to perform any sort of arithmetic on this offset;
+     * it is just a cookie which is passed to the unsafe heap memory accessors.
+     *
+     * <p>Any given field will always have the same offset, and no two distinct
+     * fields of the same class will ever have the same offset.
+     *
+     * <p>As of 1.4.1, offsets for fields are represented as long values,
+     * although the Sun JVM does not use the most significant 32 bits.
+     * It is hard to imagine a JVM technology which needs more than
+     * a few bits to encode an offset within a non-array object,
+     * However, for consistency with other methods in this class,
+     * this method reports its result as a long value.
+     * @see #getInt(Object, long)
+     */
+    public native long staticFieldOffset(Field f);
+
+    /**
+     * Reports the location of a given static field, in conjunction with {@link
+     * #staticFieldOffset}.
+     * <p>Fetch the base "Object", if any, with which static fields of the
+     * given class can be accessed via methods like {@link #getInt(Object,
+     * long)}.  This value may be null.  This value may refer to an object
+     * which is a "cookie", not guaranteed to be a real Object, and it should
+     * not be used in any way except as argument to the get and put routines in
+     * this class.
+     */
+    public native Object staticFieldBase(Field f);
+
+    /**
+     * Detects if the given class may need to be initialized. This is often
+     * needed in conjunction with obtaining the static field base of a
+     * class.
+     * @return false only if a call to {@code ensureClassInitialized} would have no effect
+     */
+    public native boolean shouldBeInitialized(Class<?> c);
+
+    /**
+     * Ensures the given class has been initialized. This is often
+     * needed in conjunction with obtaining the static field base of a
+     * class.
+     */
+    public native void ensureClassInitialized(Class<?> c);
+
+    /**
+     * Reports the offset of the first element in the storage allocation of a
+     * given array class.  If {@link #arrayIndexScale} returns a non-zero value
+     * for the same class, you may use that scale factor, together with this
+     * base offset, to form new offsets to access elements of arrays of the
+     * given class.
+     *
+     * @see #getInt(Object, long)
+     * @see #putInt(Object, long, int)
+     */
+    public native int arrayBaseOffset(Class<?> arrayClass);
+
+    /** The value of {@code arrayBaseOffset(boolean[].class)} */
+    public static final int ARRAY_BOOLEAN_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(boolean[].class);
+
+    /** The value of {@code arrayBaseOffset(byte[].class)} */
+    public static final int ARRAY_BYTE_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(byte[].class);
+
+    /** The value of {@code arrayBaseOffset(short[].class)} */
+    public static final int ARRAY_SHORT_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(short[].class);
+
+    /** The value of {@code arrayBaseOffset(char[].class)} */
+    public static final int ARRAY_CHAR_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(char[].class);
+
+    /** The value of {@code arrayBaseOffset(int[].class)} */
+    public static final int ARRAY_INT_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(int[].class);
+
+    /** The value of {@code arrayBaseOffset(long[].class)} */
+    public static final int ARRAY_LONG_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(long[].class);
+
+    /** The value of {@code arrayBaseOffset(float[].class)} */
+    public static final int ARRAY_FLOAT_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(float[].class);
+
+    /** The value of {@code arrayBaseOffset(double[].class)} */
+    public static final int ARRAY_DOUBLE_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(double[].class);
+
+    /** The value of {@code arrayBaseOffset(Object[].class)} */
+    public static final int ARRAY_OBJECT_BASE_OFFSET
+            = theUnsafe.arrayBaseOffset(Object[].class);
+
+    /**
+     * Reports the scale factor for addressing elements in the storage
+     * allocation of a given array class.  However, arrays of "narrow" types
+     * will generally not work properly with accessors like {@link
+     * #getByte(Object, long)}, so the scale factor for such classes is reported
+     * as zero.
+     *
+     * @see #arrayBaseOffset
+     * @see #getInt(Object, long)
+     * @see #putInt(Object, long, int)
+     */
+    public native int arrayIndexScale(Class<?> arrayClass);
+
+    /** The value of {@code arrayIndexScale(boolean[].class)} */
+    public static final int ARRAY_BOOLEAN_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(boolean[].class);
+
+    /** The value of {@code arrayIndexScale(byte[].class)} */
+    public static final int ARRAY_BYTE_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(byte[].class);
+
+    /** The value of {@code arrayIndexScale(short[].class)} */
+    public static final int ARRAY_SHORT_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(short[].class);
+
+    /** The value of {@code arrayIndexScale(char[].class)} */
+    public static final int ARRAY_CHAR_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(char[].class);
+
+    /** The value of {@code arrayIndexScale(int[].class)} */
+    public static final int ARRAY_INT_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(int[].class);
+
+    /** The value of {@code arrayIndexScale(long[].class)} */
+    public static final int ARRAY_LONG_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(long[].class);
+
+    /** The value of {@code arrayIndexScale(float[].class)} */
+    public static final int ARRAY_FLOAT_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(float[].class);
+
+    /** The value of {@code arrayIndexScale(double[].class)} */
+    public static final int ARRAY_DOUBLE_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(double[].class);
+
+    /** The value of {@code arrayIndexScale(Object[].class)} */
+    public static final int ARRAY_OBJECT_INDEX_SCALE
+            = theUnsafe.arrayIndexScale(Object[].class);
+
+    /**
+     * Reports the size in bytes of a native pointer, as stored via {@link
+     * #putAddress}.  This value will be either 4 or 8.  Note that the sizes of
+     * other primitive types (as stored in native memory blocks) is determined
+     * fully by their information content.
+     */
+    public native int addressSize();
+
+    /** The value of {@code addressSize()} */
+    public static final int ADDRESS_SIZE = theUnsafe.addressSize();
+
+    /**
+     * Reports the size in bytes of a native memory page (whatever that is).
+     * This value will always be a power of two.
+     */
+    public native int pageSize();
+
+
+    /// random trusted operations from JNI:
+
+    /**
+     * Tells the VM to define a class, without security checks.  By default, the
+     * class loader and protection domain come from the caller's class.
+     */
+    public native Class<?> defineClass(String name, byte[] b, int off, int len,
+                                       ClassLoader loader,
+                                       ProtectionDomain protectionDomain);
+
+    /**
+     * Defines a class but does not make it known to the class loader or system dictionary.
+     * <p>
+     * For each CP entry, the corresponding CP patch must either be null or have
+     * the a format that matches its tag:
+     * <ul>
+     * <li>Integer, Long, Float, Double: the corresponding wrapper object type from java.lang
+     * <li>Utf8: a string (must have suitable syntax if used as signature or name)
+     * <li>Class: any java.lang.Class object
+     * <li>String: any object (not just a java.lang.String)
+     * <li>InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
+     * </ul>
+     * @param hostClass context for linkage, access control, protection domain, and class loader
+     * @param data      bytes of a class file
+     * @param cpPatches where non-null entries exist, they replace corresponding CP entries in data
+     */
+    public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
+
+    /**
+     * Allocates an instance but does not run any constructor.
+     * Initializes the class if it has not yet been.
+     */
+    @HotSpotIntrinsicCandidate
+    public native Object allocateInstance(Class<?> cls)
+        throws InstantiationException;
+
+    /** Throws the exception without telling the verifier. */
+    public native void throwException(Throwable ee);
+
+    /**
+     * Atomically updates Java variable to {@code x} if it is currently
+     * holding {@code expected}.
+     *
+     * <p>This operation has memory semantics of a {@code volatile} read
+     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
+     *
+     * @return {@code true} if successful
+     */
+    @HotSpotIntrinsicCandidate
+    public final native boolean compareAndSwapObject(Object o, long offset,
+                                                     Object expected,
+                                                     Object x);
+
+    /**
+     * Atomically updates Java variable to {@code x} if it is currently
+     * holding {@code expected}.
+     *
+     * <p>This operation has memory semantics of a {@code volatile} read
+     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
+     *
+     * @return {@code true} if successful
+     */
+    @HotSpotIntrinsicCandidate
+    public final native boolean compareAndSwapInt(Object o, long offset,
+                                                  int expected,
+                                                  int x);
+
+    /**
+     * Atomically updates Java variable to {@code x} if it is currently
+     * holding {@code expected}.
+     *
+     * <p>This operation has memory semantics of a {@code volatile} read
+     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
+     *
+     * @return {@code true} if successful
+     */
+    @HotSpotIntrinsicCandidate
+    public final native boolean compareAndSwapLong(Object o, long offset,
+                                                   long expected,
+                                                   long x);
+
+    /**
+     * Fetches a reference value from a given Java variable, with volatile
+     * load semantics. Otherwise identical to {@link #getObject(Object, long)}
+     */
+    @HotSpotIntrinsicCandidate
+    public native Object getObjectVolatile(Object o, long offset);
+
+    /**
+     * Stores a reference value into a given Java variable, with
+     * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)}
+     */
+    @HotSpotIntrinsicCandidate
+    public native void    putObjectVolatile(Object o, long offset, Object x);
+
+    /** Volatile version of {@link #getInt(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native int     getIntVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putInt(Object, long, int)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putIntVolatile(Object o, long offset, int x);
+
+    /** Volatile version of {@link #getBoolean(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native boolean getBooleanVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putBoolean(Object, long, boolean)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putBooleanVolatile(Object o, long offset, boolean x);
+
+    /** Volatile version of {@link #getByte(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native byte    getByteVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putByte(Object, long, byte)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putByteVolatile(Object o, long offset, byte x);
+
+    /** Volatile version of {@link #getShort(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native short   getShortVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putShort(Object, long, short)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putShortVolatile(Object o, long offset, short x);
+
+    /** Volatile version of {@link #getChar(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native char    getCharVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putChar(Object, long, char)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putCharVolatile(Object o, long offset, char x);
+
+    /** Volatile version of {@link #getLong(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native long    getLongVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putLong(Object, long, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putLongVolatile(Object o, long offset, long x);
+
+    /** Volatile version of {@link #getFloat(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native float   getFloatVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putFloat(Object, long, float)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putFloatVolatile(Object o, long offset, float x);
+
+    /** Volatile version of {@link #getDouble(Object, long)}  */
+    @HotSpotIntrinsicCandidate
+    public native double  getDoubleVolatile(Object o, long offset);
+
+    /** Volatile version of {@link #putDouble(Object, long, double)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putDoubleVolatile(Object o, long offset, double x);
+
+    /**
+     * Version of {@link #putObjectVolatile(Object, long, Object)}
+     * that does not guarantee immediate visibility of the store to
+     * other threads. This method is generally only useful if the
+     * underlying field is a Java volatile (or if an array cell, one
+     * that is otherwise only accessed using volatile accesses).
+     *
+     * Corresponds to C11 atomic_store_explicit(..., memory_order_release).
+     */
+    @HotSpotIntrinsicCandidate
+    public native void    putOrderedObject(Object o, long offset, Object x);
+
+    /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}  */
+    @HotSpotIntrinsicCandidate
+    public native void    putOrderedInt(Object o, long offset, int x);
+
+    /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
+    @HotSpotIntrinsicCandidate
+    public native void    putOrderedLong(Object o, long offset, long x);
+
+    /**
+     * Unblocks the given thread blocked on {@code park}, or, if it is
+     * not blocked, causes the subsequent call to {@code park} not to
+     * block.  Note: this operation is "unsafe" solely because the
+     * caller must somehow ensure that the thread has not been
+     * destroyed. Nothing special is usually required to ensure this
+     * when called from Java (in which there will ordinarily be a live
+     * reference to the thread) but this is not nearly-automatically
+     * so when calling from native code.
+     *
+     * @param thread the thread to unpark.
+     */
+    @HotSpotIntrinsicCandidate
+    public native void unpark(Object thread);
+
+    /**
+     * Blocks current thread, returning when a balancing
+     * {@code unpark} occurs, or a balancing {@code unpark} has
+     * already occurred, or the thread is interrupted, or, if not
+     * absolute and time is not zero, the given time nanoseconds have
+     * elapsed, or if absolute, the given deadline in milliseconds
+     * since Epoch has passed, or spuriously (i.e., returning for no
+     * "reason"). Note: This operation is in the Unsafe class only
+     * because {@code unpark} is, so it would be strange to place it
+     * elsewhere.
+     */
+    @HotSpotIntrinsicCandidate
+    public native void park(boolean isAbsolute, long time);
+
+    /**
+     * Gets the load average in the system run queue assigned
+     * to the available processors averaged over various periods of time.
+     * This method retrieves the given {@code nelem} samples and
+     * assigns to the elements of the given {@code loadavg} array.
+     * The system imposes a maximum of 3 samples, representing
+     * averages over the last 1,  5,  and  15 minutes, respectively.
+     *
+     * @param loadavg an array of double of size nelems
+     * @param nelems the number of samples to be retrieved and
+     *        must be 1 to 3.
+     *
+     * @return the number of samples actually retrieved; or -1
+     *         if the load average is unobtainable.
+     */
+    public native int getLoadAverage(double[] loadavg, int nelems);
+
+    // The following contain CAS-based Java implementations used on
+    // platforms not supporting native instructions
+
+    /**
+     * Atomically adds the given value to the current value of a field
+     * or array element within the given object {@code o}
+     * at the given {@code offset}.
+     *
+     * @param o object/array to update the field/element in
+     * @param offset field/element offset
+     * @param delta the value to add
+     * @return the previous value
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public final int getAndAddInt(Object o, long offset, int delta) {
+        int v;
+        do {
+            v = getIntVolatile(o, offset);
+        } while (!compareAndSwapInt(o, offset, v, v + delta));
+        return v;
+    }
+
+    /**
+     * Atomically adds the given value to the current value of a field
+     * or array element within the given object {@code o}
+     * at the given {@code offset}.
+     *
+     * @param o object/array to update the field/element in
+     * @param offset field/element offset
+     * @param delta the value to add
+     * @return the previous value
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public final long getAndAddLong(Object o, long offset, long delta) {
+        long v;
+        do {
+            v = getLongVolatile(o, offset);
+        } while (!compareAndSwapLong(o, offset, v, v + delta));
+        return v;
+    }
+
+    /**
+     * Atomically exchanges the given value with the current value of
+     * a field or array element within the given object {@code o}
+     * at the given {@code offset}.
+     *
+     * @param o object/array to update the field/element in
+     * @param offset field/element offset
+     * @param newValue new value
+     * @return the previous value
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public final int getAndSetInt(Object o, long offset, int newValue) {
+        int v;
+        do {
+            v = getIntVolatile(o, offset);
+        } while (!compareAndSwapInt(o, offset, v, newValue));
+        return v;
+    }
+
+    /**
+     * Atomically exchanges the given value with the current value of
+     * a field or array element within the given object {@code o}
+     * at the given {@code offset}.
+     *
+     * @param o object/array to update the field/element in
+     * @param offset field/element offset
+     * @param newValue new value
+     * @return the previous value
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public final long getAndSetLong(Object o, long offset, long newValue) {
+        long v;
+        do {
+            v = getLongVolatile(o, offset);
+        } while (!compareAndSwapLong(o, offset, v, newValue));
+        return v;
+    }
+
+    /**
+     * Atomically exchanges the given reference value with the current
+     * reference value of a field or array element within the given
+     * object {@code o} at the given {@code offset}.
+     *
+     * @param o object/array to update the field/element in
+     * @param offset field/element offset
+     * @param newValue new value
+     * @return the previous value
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public final Object getAndSetObject(Object o, long offset, Object newValue) {
+        Object v;
+        do {
+            v = getObjectVolatile(o, offset);
+        } while (!compareAndSwapObject(o, offset, v, newValue));
+        return v;
+    }
+
+
+    /**
+     * Ensures that loads before the fence will not be reordered with loads and
+     * stores after the fence; a "LoadLoad plus LoadStore barrier".
+     *
+     * Corresponds to C11 atomic_thread_fence(memory_order_acquire)
+     * (an "acquire fence").
+     *
+     * A pure LoadLoad fence is not provided, since the addition of LoadStore
+     * is almost always desired, and most current hardware instructions that
+     * provide a LoadLoad barrier also provide a LoadStore barrier for free.
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public native void loadFence();
+
+    /**
+     * Ensures that loads and stores before the fence will not be reordered with
+     * stores after the fence; a "StoreStore plus LoadStore barrier".
+     *
+     * Corresponds to C11 atomic_thread_fence(memory_order_release)
+     * (a "release fence").
+     *
+     * A pure StoreStore fence is not provided, since the addition of LoadStore
+     * is almost always desired, and most current hardware instructions that
+     * provide a StoreStore barrier also provide a LoadStore barrier for free.
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public native void storeFence();
+
+    /**
+     * Ensures that loads and stores before the fence will not be reordered
+     * with loads and stores after the fence.  Implies the effects of both
+     * loadFence() and storeFence(), and in addition, the effect of a StoreLoad
+     * barrier.
+     *
+     * Corresponds to C11 atomic_thread_fence(memory_order_seq_cst).
+     * @since 1.8
+     */
+    @HotSpotIntrinsicCandidate
+    public native void fullFence();
+
+    /**
+     * Throws IllegalAccessError; for use by the VM for access control
+     * error support.
+     * @since 1.8
+     */
+    private static void throwIllegalAccessError() {
+        throw new IllegalAccessError();
+    }
+
+    /**
+     * @return Returns true if the native byte ordering of this
+     * platform is big-endian, false if it is little-endian.
+     */
+    public final boolean isBigEndian() { return BE; }
+
+    /**
+     * @return Returns true if this platform is capable of performing
+     * accesses at addresses which are not aligned for the type of the
+     * primitive type being accessed, false otherwise.
+     */
+    public final boolean unalignedAccess() { return unalignedAccess; }
+
+    /**
+     * Fetches a value at some byte offset into a given Java object.
+     * More specifically, fetches a value within the given object
+     * <code>o</code> at the given offset, or (if <code>o</code> is
+     * null) from the memory address whose numerical value is the
+     * given offset.  <p>
+     *
+     * The specification of this method is the same as {@link
+     * #getLong(Object, long)} except that the offset does not need to
+     * have been obtained from {@link #objectFieldOffset} on the
+     * {@link java.lang.reflect.Field} of some Java field.  The value
+     * in memory is raw data, and need not correspond to any Java
+     * variable.  Unless <code>o</code> is null, the value accessed
+     * must be entirely within the allocated object.  The endianness
+     * of the value in memory is the endianness of the native platform.
+     *
+     * <p> The read will be atomic with respect to the largest power
+     * of two that divides the GCD of the offset and the storage size.
+     * For example, getLongUnaligned will make atomic reads of 2-, 4-,
+     * or 8-byte storage units if the offset is zero mod 2, 4, or 8,
+     * respectively.  There are no other guarantees of atomicity.
+     * <p>
+     * 8-byte atomicity is only guaranteed on platforms on which
+     * support atomic accesses to longs.
+     *
+     * @param o Java heap object in which the value resides, if any, else
+     *        null
+     * @param offset The offset in bytes from the start of the object
+     * @return the value fetched from the indicated object
+     * @throws RuntimeException No defined exceptions are thrown, not even
+     *         {@link NullPointerException}
+     * @since 1.9
+     */
+    @HotSpotIntrinsicCandidate
+    public final long getLongUnaligned(Object o, long offset) {
+        if ((offset & 7) == 0) {
+            return getLong(o, offset);
+        } else if ((offset & 3) == 0) {
+            return makeLong(getInt(o, offset),
+                            getInt(o, offset + 4));
+        } else if ((offset & 1) == 0) {
+            return makeLong(getShort(o, offset),
+                            getShort(o, offset + 2),
+                            getShort(o, offset + 4),
+                            getShort(o, offset + 6));
+        } else {
+            return makeLong(getByte(o, offset),
+                            getByte(o, offset + 1),
+                            getByte(o, offset + 2),
+                            getByte(o, offset + 3),
+                            getByte(o, offset + 4),
+                            getByte(o, offset + 5),
+                            getByte(o, offset + 6),
+                            getByte(o, offset + 7));
+        }
+    }
+    /**
+     * As {@link #getLongUnaligned(Object, long)} but with an
+     * additional argument which specifies the endianness of the value
+     * as stored in memory.
+     *
+     * @param o Java heap object in which the variable resides
+     * @param offset The offset in bytes from the start of the object
+     * @param bigEndian The endianness of the value
+     * @return the value fetched from the indicated object
+     * @since 1.9
+     */
+    public final long getLongUnaligned(Object o, long offset, boolean bigEndian) {
+        return convEndian(bigEndian, getLongUnaligned(o, offset));
+    }
+
+    /** @see #getLongUnaligned(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public final int getIntUnaligned(Object o, long offset) {
+        if ((offset & 3) == 0) {
+            return getInt(o, offset);
+        } else if ((offset & 1) == 0) {
+            return makeInt(getShort(o, offset),
+                           getShort(o, offset + 2));
+        } else {
+            return makeInt(getByte(o, offset),
+                           getByte(o, offset + 1),
+                           getByte(o, offset + 2),
+                           getByte(o, offset + 3));
+        }
+    }
+    /** @see #getLongUnaligned(Object, long, boolean) */
+    public final int getIntUnaligned(Object o, long offset, boolean bigEndian) {
+        return convEndian(bigEndian, getIntUnaligned(o, offset));
+    }
+
+    /** @see #getLongUnaligned(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public final short getShortUnaligned(Object o, long offset) {
+        if ((offset & 1) == 0) {
+            return getShort(o, offset);
+        } else {
+            return makeShort(getByte(o, offset),
+                             getByte(o, offset + 1));
+        }
+    }
+    /** @see #getLongUnaligned(Object, long, boolean) */
+    public final short getShortUnaligned(Object o, long offset, boolean bigEndian) {
+        return convEndian(bigEndian, getShortUnaligned(o, offset));
+    }
+
+    /** @see #getLongUnaligned(Object, long) */
+    @HotSpotIntrinsicCandidate
+    public final char getCharUnaligned(Object o, long offset) {
+        return (char)getShortUnaligned(o, offset);
+    }
+
+    /** @see #getLongUnaligned(Object, long, boolean) */
+    public final char getCharUnaligned(Object o, long offset, boolean bigEndian) {
+        return convEndian(bigEndian, getCharUnaligned(o, offset));
+    }
+
+    /**
+     * Stores a value at some byte offset into a given Java object.
+     * <p>
+     * The specification of this method is the same as {@link
+     * #getLong(Object, long)} except that the offset does not need to
+     * have been obtained from {@link #objectFieldOffset} on the
+     * {@link java.lang.reflect.Field} of some Java field.  The value
+     * in memory is raw data, and need not correspond to any Java
+     * variable.  The endianness of the value in memory is the
+     * endianness of the native platform.
+     * <p>
+     * The write will be atomic with respect to the largest power of
+     * two that divides the GCD of the offset and the storage size.
+     * For example, putLongUnaligned will make atomic writes of 2-, 4-,
+     * or 8-byte storage units if the offset is zero mod 2, 4, or 8,
+     * respectively.  There are no other guarantees of atomicity.
+     * <p>
+     * 8-byte atomicity is only guaranteed on platforms on which
+     * support atomic accesses to longs.
+     *
+     * @param o Java heap object in which the value resides, if any, else
+     *        null
+     * @param offset The offset in bytes from the start of the object
+     * @param x the value to store
+     * @throws RuntimeException No defined exceptions are thrown, not even
+     *         {@link NullPointerException}
+     * @since 1.9
+     */
+    @HotSpotIntrinsicCandidate
+    public final void putLongUnaligned(Object o, long offset, long x) {
+        if ((offset & 7) == 0) {
+            putLong(o, offset, x);
+        } else if ((offset & 3) == 0) {
+            putLongParts(o, offset,
+                         (int)(x >> 0),
+                         (int)(x >>> 32));
+        } else if ((offset & 1) == 0) {
+            putLongParts(o, offset,
+                         (short)(x >>> 0),
+                         (short)(x >>> 16),
+                         (short)(x >>> 32),
+                         (short)(x >>> 48));
+        } else {
+            putLongParts(o, offset,
+                         (byte)(x >>> 0),
+                         (byte)(x >>> 8),
+                         (byte)(x >>> 16),
+                         (byte)(x >>> 24),
+                         (byte)(x >>> 32),
+                         (byte)(x >>> 40),
+                         (byte)(x >>> 48),
+                         (byte)(x >>> 56));
+        }
+    }
+
+    /**
+     * As {@link #putLongUnaligned(Object, long, long)} but with an additional
+     * argument which specifies the endianness of the value as stored in memory.
+     * @param o Java heap object in which the value resides
+     * @param offset The offset in bytes from the start of the object
+     * @param x the value to store
+     * @param bigEndian The endianness of the value
+     * @throws RuntimeException No defined exceptions are thrown, not even
+     *         {@link NullPointerException}
+     * @since 1.9
+     */
+    public final void putLongUnaligned(Object o, long offset, long x, boolean bigEndian) {
+        putLongUnaligned(o, offset, convEndian(bigEndian, x));
+    }
+
+    /** @see #putLongUnaligned(Object, long, long) */
+    @HotSpotIntrinsicCandidate
+    public final void putIntUnaligned(Object o, long offset, int x) {
+        if ((offset & 3) == 0) {
+            putInt(o, offset, x);
+        } else if ((offset & 1) == 0) {
+            putIntParts(o, offset,
+                        (short)(x >> 0),
+                        (short)(x >>> 16));
+        } else {
+            putIntParts(o, offset,
+                        (byte)(x >>> 0),
+                        (byte)(x >>> 8),
+                        (byte)(x >>> 16),
+                        (byte)(x >>> 24));
+        }
+    }
+    /** @see #putLongUnaligned(Object, long, long, boolean) */
+    public final void putIntUnaligned(Object o, long offset, int x, boolean bigEndian) {
+        putIntUnaligned(o, offset, convEndian(bigEndian, x));
+    }
+
+    /** @see #putLongUnaligned(Object, long, long) */
+    @HotSpotIntrinsicCandidate
+    public final void putShortUnaligned(Object o, long offset, short x) {
+        if ((offset & 1) == 0) {
+            putShort(o, offset, x);
+        } else {
+            putShortParts(o, offset,
+                          (byte)(x >>> 0),
+                          (byte)(x >>> 8));
+        }
+    }
+    /** @see #putLongUnaligned(Object, long, long, boolean) */
+    public final void putShortUnaligned(Object o, long offset, short x, boolean bigEndian) {
+        putShortUnaligned(o, offset, convEndian(bigEndian, x));
+    }
+
+    /** @see #putLongUnaligned(Object, long, long) */
+    @HotSpotIntrinsicCandidate
+    public final void putCharUnaligned(Object o, long offset, char x) {
+        putShortUnaligned(o, offset, (short)x);
+    }
+    /** @see #putLongUnaligned(Object, long, long, boolean) */
+    public final void putCharUnaligned(Object o, long offset, char x, boolean bigEndian) {
+        putCharUnaligned(o, offset, convEndian(bigEndian, x));
+    }
+
+    // JVM interface methods
+    private native boolean unalignedAccess0();
+    private native boolean isBigEndian0();
+
+    // BE is true iff the native endianness of this platform is big.
+    private static final boolean BE = theUnsafe.isBigEndian0();
+
+    // unalignedAccess is true iff this platform can perform unaligned accesses.
+    private static final boolean unalignedAccess = theUnsafe.unalignedAccess0();
+
+    private static int pickPos(int top, int pos) { return BE ? top - pos : pos; }
+
+    // These methods construct integers from bytes.  The byte ordering
+    // is the native endianness of this platform.
+    private static long makeLong(byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) {
+        return ((toUnsignedLong(i0) << pickPos(56, 0))
+              | (toUnsignedLong(i1) << pickPos(56, 8))
+              | (toUnsignedLong(i2) << pickPos(56, 16))
+              | (toUnsignedLong(i3) << pickPos(56, 24))
+              | (toUnsignedLong(i4) << pickPos(56, 32))
+              | (toUnsignedLong(i5) << pickPos(56, 40))
+              | (toUnsignedLong(i6) << pickPos(56, 48))
+              | (toUnsignedLong(i7) << pickPos(56, 56)));
+    }
+    private static long makeLong(short i0, short i1, short i2, short i3) {
+        return ((toUnsignedLong(i0) << pickPos(48, 0))
+              | (toUnsignedLong(i1) << pickPos(48, 16))
+              | (toUnsignedLong(i2) << pickPos(48, 32))
+              | (toUnsignedLong(i3) << pickPos(48, 48)));
+    }
+    private static long makeLong(int i0, int i1) {
+        return (toUnsignedLong(i0) << pickPos(32, 0))
+             | (toUnsignedLong(i1) << pickPos(32, 32));
+    }
+    private static int makeInt(short i0, short i1) {
+        return (toUnsignedInt(i0) << pickPos(16, 0))
+             | (toUnsignedInt(i1) << pickPos(16, 16));
+    }
+    private static int makeInt(byte i0, byte i1, byte i2, byte i3) {
+        return ((toUnsignedInt(i0) << pickPos(24, 0))
+              | (toUnsignedInt(i1) << pickPos(24, 8))
+              | (toUnsignedInt(i2) << pickPos(24, 16))
+              | (toUnsignedInt(i3) << pickPos(24, 24)));
+    }
+    private static short makeShort(byte i0, byte i1) {
+        return (short)((toUnsignedInt(i0) << pickPos(8, 0))
+                     | (toUnsignedInt(i1) << pickPos(8, 8)));
+    }
+
+    private static byte  pick(byte  le, byte  be) { return BE ? be : le; }
+    private static short pick(short le, short be) { return BE ? be : le; }
+    private static int   pick(int   le, int   be) { return BE ? be : le; }
+
+    // These methods write integers to memory from smaller parts
+    // provided by their caller.  The ordering in which these parts
+    // are written is the native endianness of this platform.
+    private void putLongParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) {
+        putByte(o, offset + 0, pick(i0, i7));
+        putByte(o, offset + 1, pick(i1, i6));
+        putByte(o, offset + 2, pick(i2, i5));
+        putByte(o, offset + 3, pick(i3, i4));
+        putByte(o, offset + 4, pick(i4, i3));
+        putByte(o, offset + 5, pick(i5, i2));
+        putByte(o, offset + 6, pick(i6, i1));
+        putByte(o, offset + 7, pick(i7, i0));
+    }
+    private void putLongParts(Object o, long offset, short i0, short i1, short i2, short i3) {
+        putShort(o, offset + 0, pick(i0, i3));
+        putShort(o, offset + 2, pick(i1, i2));
+        putShort(o, offset + 4, pick(i2, i1));
+        putShort(o, offset + 6, pick(i3, i0));
+    }
+    private void putLongParts(Object o, long offset, int i0, int i1) {
+        putInt(o, offset + 0, pick(i0, i1));
+        putInt(o, offset + 4, pick(i1, i0));
+    }
+    private void putIntParts(Object o, long offset, short i0, short i1) {
+        putShort(o, offset + 0, pick(i0, i1));
+        putShort(o, offset + 2, pick(i1, i0));
+    }
+    private void putIntParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3) {
+        putByte(o, offset + 0, pick(i0, i3));
+        putByte(o, offset + 1, pick(i1, i2));
+        putByte(o, offset + 2, pick(i2, i1));
+        putByte(o, offset + 3, pick(i3, i0));
+    }
+    private void putShortParts(Object o, long offset, byte i0, byte i1) {
+        putByte(o, offset + 0, pick(i0, i1));
+        putByte(o, offset + 1, pick(i1, i0));
+    }
+
+    // Zero-extend an integer
+    private static int toUnsignedInt(byte n)    { return n & 0xff; }
+    private static int toUnsignedInt(short n)   { return n & 0xffff; }
+    private static long toUnsignedLong(byte n)  { return n & 0xffl; }
+    private static long toUnsignedLong(short n) { return n & 0xffffl; }
+    private static long toUnsignedLong(int n)   { return n & 0xffffffffl; }
+
+    // Maybe byte-reverse an integer
+    private static char convEndian(boolean big, char n)   { return big == BE ? n : Character.reverseBytes(n); }
+    private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n)    ; }
+    private static int convEndian(boolean big, int n)     { return big == BE ? n : Integer.reverseBytes(n)  ; }
+    private static long convEndian(boolean big, long n)   { return big == BE ? n : Long.reverseBytes(n)     ; }
+}
--- a/src/java.base/share/classes/sun/misc/Unsafe.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/misc/Unsafe.java	Mon Nov 09 20:37:05 2015 -0800
@@ -1036,355 +1036,8 @@
         throw new IllegalAccessError();
     }
 
-    /**
-     * @return Returns true if the native byte ordering of this
-     * platform is big-endian, false if it is little-endian.
-     */
-    public final boolean isBigEndian() { return BE; }
-
-    /**
-     * @return Returns true if this platform is capable of performing
-     * accesses at addresses which are not aligned for the type of the
-     * primitive type being accessed, false otherwise.
-     */
-    public final boolean unalignedAccess() { return unalignedAccess; }
-
-    /**
-     * Fetches a value at some byte offset into a given Java object.
-     * More specifically, fetches a value within the given object
-     * <code>o</code> at the given offset, or (if <code>o</code> is
-     * null) from the memory address whose numerical value is the
-     * given offset.  <p>
-     *
-     * The specification of this method is the same as {@link
-     * #getLong(Object, long)} except that the offset does not need to
-     * have been obtained from {@link #objectFieldOffset} on the
-     * {@link java.lang.reflect.Field} of some Java field.  The value
-     * in memory is raw data, and need not correspond to any Java
-     * variable.  Unless <code>o</code> is null, the value accessed
-     * must be entirely within the allocated object.  The endianness
-     * of the value in memory is the endianness of the native platform.
-     *
-     * <p> The read will be atomic with respect to the largest power
-     * of two that divides the GCD of the offset and the storage size.
-     * For example, getLongUnaligned will make atomic reads of 2-, 4-,
-     * or 8-byte storage units if the offset is zero mod 2, 4, or 8,
-     * respectively.  There are no other guarantees of atomicity.
-     * <p>
-     * 8-byte atomicity is only guaranteed on platforms on which
-     * support atomic accesses to longs.
-     *
-     * @param o Java heap object in which the value resides, if any, else
-     *        null
-     * @param offset The offset in bytes from the start of the object
-     * @return the value fetched from the indicated object
-     * @throws RuntimeException No defined exceptions are thrown, not even
-     *         {@link NullPointerException}
-     * @since 1.9
-     */
-    @HotSpotIntrinsicCandidate
-    public final long getLongUnaligned(Object o, long offset) {
-        if ((offset & 7) == 0) {
-            return getLong(o, offset);
-        } else if ((offset & 3) == 0) {
-            return makeLong(getInt(o, offset),
-                            getInt(o, offset + 4));
-        } else if ((offset & 1) == 0) {
-            return makeLong(getShort(o, offset),
-                            getShort(o, offset + 2),
-                            getShort(o, offset + 4),
-                            getShort(o, offset + 6));
-        } else {
-            return makeLong(getByte(o, offset),
-                            getByte(o, offset + 1),
-                            getByte(o, offset + 2),
-                            getByte(o, offset + 3),
-                            getByte(o, offset + 4),
-                            getByte(o, offset + 5),
-                            getByte(o, offset + 6),
-                            getByte(o, offset + 7));
-        }
-    }
-    /**
-     * As {@link #getLongUnaligned(Object, long)} but with an
-     * additional argument which specifies the endianness of the value
-     * as stored in memory.
-     *
-     * @param o Java heap object in which the variable resides
-     * @param offset The offset in bytes from the start of the object
-     * @param bigEndian The endianness of the value
-     * @return the value fetched from the indicated object
-     * @since 1.9
-     */
-    public final long getLongUnaligned(Object o, long offset, boolean bigEndian) {
-        return convEndian(bigEndian, getLongUnaligned(o, offset));
-    }
-
-    /** @see #getLongUnaligned(Object, long) */
-    @HotSpotIntrinsicCandidate
-    public final int getIntUnaligned(Object o, long offset) {
-        if ((offset & 3) == 0) {
-            return getInt(o, offset);
-        } else if ((offset & 1) == 0) {
-            return makeInt(getShort(o, offset),
-                           getShort(o, offset + 2));
-        } else {
-            return makeInt(getByte(o, offset),
-                           getByte(o, offset + 1),
-                           getByte(o, offset + 2),
-                           getByte(o, offset + 3));
-        }
-    }
-    /** @see #getLongUnaligned(Object, long, boolean) */
-    public final int getIntUnaligned(Object o, long offset, boolean bigEndian) {
-        return convEndian(bigEndian, getIntUnaligned(o, offset));
-    }
-
-    /** @see #getLongUnaligned(Object, long) */
-    @HotSpotIntrinsicCandidate
-    public final short getShortUnaligned(Object o, long offset) {
-        if ((offset & 1) == 0) {
-            return getShort(o, offset);
-        } else {
-            return makeShort(getByte(o, offset),
-                             getByte(o, offset + 1));
-        }
-    }
-    /** @see #getLongUnaligned(Object, long, boolean) */
-    public final short getShortUnaligned(Object o, long offset, boolean bigEndian) {
-        return convEndian(bigEndian, getShortUnaligned(o, offset));
-    }
-
-    /** @see #getLongUnaligned(Object, long) */
-    @HotSpotIntrinsicCandidate
-    public final char getCharUnaligned(Object o, long offset) {
-        return (char)getShortUnaligned(o, offset);
-    }
-
-    /** @see #getLongUnaligned(Object, long, boolean) */
-    public final char getCharUnaligned(Object o, long offset, boolean bigEndian) {
-        return convEndian(bigEndian, getCharUnaligned(o, offset));
-    }
-
-    /**
-     * Stores a value at some byte offset into a given Java object.
-     * <p>
-     * The specification of this method is the same as {@link
-     * #getLong(Object, long)} except that the offset does not need to
-     * have been obtained from {@link #objectFieldOffset} on the
-     * {@link java.lang.reflect.Field} of some Java field.  The value
-     * in memory is raw data, and need not correspond to any Java
-     * variable.  The endianness of the value in memory is the
-     * endianness of the native platform.
-     * <p>
-     * The write will be atomic with respect to the largest power of
-     * two that divides the GCD of the offset and the storage size.
-     * For example, putLongUnaligned will make atomic writes of 2-, 4-,
-     * or 8-byte storage units if the offset is zero mod 2, 4, or 8,
-     * respectively.  There are no other guarantees of atomicity.
-     * <p>
-     * 8-byte atomicity is only guaranteed on platforms on which
-     * support atomic accesses to longs.
-     *
-     * @param o Java heap object in which the value resides, if any, else
-     *        null
-     * @param offset The offset in bytes from the start of the object
-     * @param x the value to store
-     * @throws RuntimeException No defined exceptions are thrown, not even
-     *         {@link NullPointerException}
-     * @since 1.9
-     */
-    @HotSpotIntrinsicCandidate
-    public final void putLongUnaligned(Object o, long offset, long x) {
-        if ((offset & 7) == 0) {
-            putLong(o, offset, x);
-        } else if ((offset & 3) == 0) {
-            putLongParts(o, offset,
-                         (int)(x >> 0),
-                         (int)(x >>> 32));
-        } else if ((offset & 1) == 0) {
-            putLongParts(o, offset,
-                         (short)(x >>> 0),
-                         (short)(x >>> 16),
-                         (short)(x >>> 32),
-                         (short)(x >>> 48));
-        } else {
-            putLongParts(o, offset,
-                         (byte)(x >>> 0),
-                         (byte)(x >>> 8),
-                         (byte)(x >>> 16),
-                         (byte)(x >>> 24),
-                         (byte)(x >>> 32),
-                         (byte)(x >>> 40),
-                         (byte)(x >>> 48),
-                         (byte)(x >>> 56));
-        }
-    }
-
-    /**
-     * As {@link #putLongUnaligned(Object, long, long)} but with an additional
-     * argument which specifies the endianness of the value as stored in memory.
-     * @param o Java heap object in which the value resides
-     * @param offset The offset in bytes from the start of the object
-     * @param x the value to store
-     * @param bigEndian The endianness of the value
-     * @throws RuntimeException No defined exceptions are thrown, not even
-     *         {@link NullPointerException}
-     * @since 1.9
-     */
-    public final void putLongUnaligned(Object o, long offset, long x, boolean bigEndian) {
-        putLongUnaligned(o, offset, convEndian(bigEndian, x));
-    }
-
-    /** @see #putLongUnaligned(Object, long, long) */
-    @HotSpotIntrinsicCandidate
-    public final void putIntUnaligned(Object o, long offset, int x) {
-        if ((offset & 3) == 0) {
-            putInt(o, offset, x);
-        } else if ((offset & 1) == 0) {
-            putIntParts(o, offset,
-                        (short)(x >> 0),
-                        (short)(x >>> 16));
-        } else {
-            putIntParts(o, offset,
-                        (byte)(x >>> 0),
-                        (byte)(x >>> 8),
-                        (byte)(x >>> 16),
-                        (byte)(x >>> 24));
-        }
-    }
-    /** @see #putLongUnaligned(Object, long, long, boolean) */
-    public final void putIntUnaligned(Object o, long offset, int x, boolean bigEndian) {
-        putIntUnaligned(o, offset, convEndian(bigEndian, x));
-    }
-
-    /** @see #putLongUnaligned(Object, long, long) */
-    @HotSpotIntrinsicCandidate
-    public final void putShortUnaligned(Object o, long offset, short x) {
-        if ((offset & 1) == 0) {
-            putShort(o, offset, x);
-        } else {
-            putShortParts(o, offset,
-                          (byte)(x >>> 0),
-                          (byte)(x >>> 8));
-        }
-    }
-    /** @see #putLongUnaligned(Object, long, long, boolean) */
-    public final void putShortUnaligned(Object o, long offset, short x, boolean bigEndian) {
-        putShortUnaligned(o, offset, convEndian(bigEndian, x));
-    }
-
-    /** @see #putLongUnaligned(Object, long, long) */
-    @HotSpotIntrinsicCandidate
-    public final void putCharUnaligned(Object o, long offset, char x) {
-        putShortUnaligned(o, offset, (short)x);
-    }
-    /** @see #putLongUnaligned(Object, long, long, boolean) */
-    public final void putCharUnaligned(Object o, long offset, char x, boolean bigEndian) {
-        putCharUnaligned(o, offset, convEndian(bigEndian, x));
-    }
-
     // JVM interface methods
     private native boolean unalignedAccess0();
     private native boolean isBigEndian0();
 
-    // BE is true iff the native endianness of this platform is big.
-    private static final boolean BE = theUnsafe.isBigEndian0();
-
-    // unalignedAccess is true iff this platform can perform unaligned accesses.
-    private static final boolean unalignedAccess = theUnsafe.unalignedAccess0();
-
-    private static int pickPos(int top, int pos) { return BE ? top - pos : pos; }
-
-    // These methods construct integers from bytes.  The byte ordering
-    // is the native endianness of this platform.
-    private static long makeLong(byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) {
-        return ((toUnsignedLong(i0) << pickPos(56, 0))
-              | (toUnsignedLong(i1) << pickPos(56, 8))
-              | (toUnsignedLong(i2) << pickPos(56, 16))
-              | (toUnsignedLong(i3) << pickPos(56, 24))
-              | (toUnsignedLong(i4) << pickPos(56, 32))
-              | (toUnsignedLong(i5) << pickPos(56, 40))
-              | (toUnsignedLong(i6) << pickPos(56, 48))
-              | (toUnsignedLong(i7) << pickPos(56, 56)));
-    }
-    private static long makeLong(short i0, short i1, short i2, short i3) {
-        return ((toUnsignedLong(i0) << pickPos(48, 0))
-              | (toUnsignedLong(i1) << pickPos(48, 16))
-              | (toUnsignedLong(i2) << pickPos(48, 32))
-              | (toUnsignedLong(i3) << pickPos(48, 48)));
-    }
-    private static long makeLong(int i0, int i1) {
-        return (toUnsignedLong(i0) << pickPos(32, 0))
-             | (toUnsignedLong(i1) << pickPos(32, 32));
-    }
-    private static int makeInt(short i0, short i1) {
-        return (toUnsignedInt(i0) << pickPos(16, 0))
-             | (toUnsignedInt(i1) << pickPos(16, 16));
-    }
-    private static int makeInt(byte i0, byte i1, byte i2, byte i3) {
-        return ((toUnsignedInt(i0) << pickPos(24, 0))
-              | (toUnsignedInt(i1) << pickPos(24, 8))
-              | (toUnsignedInt(i2) << pickPos(24, 16))
-              | (toUnsignedInt(i3) << pickPos(24, 24)));
-    }
-    private static short makeShort(byte i0, byte i1) {
-        return (short)((toUnsignedInt(i0) << pickPos(8, 0))
-                     | (toUnsignedInt(i1) << pickPos(8, 8)));
-    }
-
-    private static byte  pick(byte  le, byte  be) { return BE ? be : le; }
-    private static short pick(short le, short be) { return BE ? be : le; }
-    private static int   pick(int   le, int   be) { return BE ? be : le; }
-
-    // These methods write integers to memory from smaller parts
-    // provided by their caller.  The ordering in which these parts
-    // are written is the native endianness of this platform.
-    private void putLongParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) {
-        putByte(o, offset + 0, pick(i0, i7));
-        putByte(o, offset + 1, pick(i1, i6));
-        putByte(o, offset + 2, pick(i2, i5));
-        putByte(o, offset + 3, pick(i3, i4));
-        putByte(o, offset + 4, pick(i4, i3));
-        putByte(o, offset + 5, pick(i5, i2));
-        putByte(o, offset + 6, pick(i6, i1));
-        putByte(o, offset + 7, pick(i7, i0));
-    }
-    private void putLongParts(Object o, long offset, short i0, short i1, short i2, short i3) {
-        putShort(o, offset + 0, pick(i0, i3));
-        putShort(o, offset + 2, pick(i1, i2));
-        putShort(o, offset + 4, pick(i2, i1));
-        putShort(o, offset + 6, pick(i3, i0));
-    }
-    private void putLongParts(Object o, long offset, int i0, int i1) {
-        putInt(o, offset + 0, pick(i0, i1));
-        putInt(o, offset + 4, pick(i1, i0));
-    }
-    private void putIntParts(Object o, long offset, short i0, short i1) {
-        putShort(o, offset + 0, pick(i0, i1));
-        putShort(o, offset + 2, pick(i1, i0));
-    }
-    private void putIntParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3) {
-        putByte(o, offset + 0, pick(i0, i3));
-        putByte(o, offset + 1, pick(i1, i2));
-        putByte(o, offset + 2, pick(i2, i1));
-        putByte(o, offset + 3, pick(i3, i0));
-    }
-    private void putShortParts(Object o, long offset, byte i0, byte i1) {
-        putByte(o, offset + 0, pick(i0, i1));
-        putByte(o, offset + 1, pick(i1, i0));
-    }
-
-    // Zero-extend an integer
-    private static int toUnsignedInt(byte n)    { return n & 0xff; }
-    private static int toUnsignedInt(short n)   { return n & 0xffff; }
-    private static long toUnsignedLong(byte n)  { return n & 0xffl; }
-    private static long toUnsignedLong(short n) { return n & 0xffffl; }
-    private static long toUnsignedLong(int n)   { return n & 0xffffffffl; }
-
-    // Maybe byte-reverse an integer
-    private static char convEndian(boolean big, char n)   { return big == BE ? n : Character.reverseBytes(n); }
-    private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n)    ; }
-    private static int convEndian(boolean big, int n)     { return big == BE ? n : Integer.reverseBytes(n)  ; }
-    private static long convEndian(boolean big, long n)   { return big == BE ? n : Long.reverseBytes(n)     ; }
 }
--- a/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java	Mon Nov 09 20:37:05 2015 -0800
@@ -32,4 +32,8 @@
 
 public interface ArrayDecoder {
     int decode(byte[] src, int off, int len, char[] dst);
+
+    default boolean isASCIICompatible() {
+        return false;
+    }
 }
--- a/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java	Mon Nov 09 20:37:05 2015 -0800
@@ -26,10 +26,24 @@
 package sun.nio.cs;
 
 /*
- * FastPath char[]->byte[] encoder, REPLACE on malformed input or
+ * FastPath char[]/byte[] -> byte[] encoder, REPLACE on malformed input or
  * unmappable input.
  */
 
 public interface ArrayEncoder {
+
+    //  is only used by j.u.zip.ZipCoder for utf8
     int encode(char[] src, int off, int len, byte[] dst);
+
+    default int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+        return -1;
+    }
+
+    default int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+        return -1;
+    }
+
+    default boolean isASCIICompatible() {
+        return false;
+    }
 }
--- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java	Mon Nov 09 20:37:05 2015 -0800
@@ -115,6 +115,7 @@
         final char[] b2cSB;
         final int b2Min;
         final int b2Max;
+        final boolean isASCIICompatible;
 
         // for SimpleEUC override
         protected CoderResult crMalformedOrUnderFlow(int b) {
@@ -132,16 +133,23 @@
 
         public Decoder(Charset cs, float avgcpb, float maxcpb,
                        char[][] b2c, char[] b2cSB,
-                       int b2Min, int b2Max) {
+                       int b2Min, int b2Max,
+                       boolean isASCIICompatible) {
             super(cs, avgcpb, maxcpb);
             this.b2c = b2c;
             this.b2cSB = b2cSB;
             this.b2Min = b2Min;
             this.b2Max = b2Max;
+            this.isASCIICompatible = isASCIICompatible;
+        }
+
+        public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                       boolean isASCIICompatible) {
+            this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
         }
 
         public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max);
+            this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, false);
         }
 
         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
@@ -215,6 +223,7 @@
                 return decodeBufferLoop(src, dst);
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -230,12 +239,12 @@
                             if (b2c[b1] == B2C_UNMAPPABLE ||  // isNotLeadingByte
                                 b2c[b2] != B2C_UNMAPPABLE ||  // isLeadingByte
                                 decodeSingle(b2) != UNMAPPABLE_DECODING) {
-                                sp--;
+                               sp--;
                             }
                         }
                     }
                     if (c == UNMAPPABLE_DECODING) {
-                        c = repl;
+                         c = repl;
                     }
                 }
                 dst[dp++] = c;
@@ -243,6 +252,11 @@
             return dp;
         }
 
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
+
         public void implReset() {
             super.implReset();
         }
@@ -274,8 +288,14 @@
         private int  currentState;
 
         public Decoder_EBCDIC(Charset cs,
-                       char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            super(cs, b2c, b2cSB, b2Min, b2Max);
+                              char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                              boolean isASCIICompatible) {
+            super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
+        }
+
+        public Decoder_EBCDIC(Charset cs,
+                              char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
+            super(cs, b2c, b2cSB, b2Min, b2Max, false);
         }
 
         public void implReset() {
@@ -403,6 +423,7 @@
             }
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -451,8 +472,13 @@
             b2cSB_UNMAPPABLE = new char[0x100];
             Arrays.fill(b2cSB_UNMAPPABLE, UNMAPPABLE_DECODING);
         }
+        public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                                boolean isASCIICompatible) {
+            super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, isASCIICompatible);
+        }
+
         public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max);
+            super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, false);
         }
     }
 
@@ -464,8 +490,9 @@
         private final int SS3 =  0x8F;
 
         public Decoder_EUC_SIM(Charset cs,
-                        char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            super(cs, b2c, b2cSB, b2Min, b2Max);
+                               char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                               boolean isASCIICompatible) {
+            super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
         }
 
         // No support provided for G2/G3 for SimpleEUC
@@ -481,6 +508,7 @@
             return CoderResult.unmappableForLength(2);
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -515,17 +543,25 @@
         private final char[] c2b;
         private final char[] c2bIndex;
         protected Surrogate.Parser sgp;
+        final boolean isASCIICompatible;
 
         public Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
+            this(cs, c2b, c2bIndex, false);
+        }
+
+        public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) {
             super(cs, 2.0f, 2.0f);
             this.c2b = c2b;
             this.c2bIndex = c2bIndex;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
-        public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) {
+        public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex,
+                       boolean isASCIICompatible) {
             super(cs, avg, max, repl);
             this.c2b = c2b;
             this.c2bIndex = c2bIndex;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
         public boolean canEncode(char c) {
@@ -624,6 +660,7 @@
             repl = newReplacement;
         }
 
+        @Override
         public int encode(char[] src, int sp, int len, byte[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -647,11 +684,69 @@
                 } else {                          // SingleByte
                     dst[dp++] = (byte)bb;
                 }
+            }
+            return dp;
+        }
+
+        @Override
+        public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = (char)(src[sp++] & 0xff);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    // no surrogate pair in latin1 string
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1) {
+                        dst[dp++] = repl[1];
+                    }
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) { // DoubleByte
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                   // SingleByte
+                    dst[dp++] = (byte)bb;
+                }
 
             }
             return dp;
         }
 
+        @Override
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    if (Character.isHighSurrogate(c) && sp < sl &&
+                        Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
+                        sp++;
+                    }
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1) {
+                        dst[dp++] = repl[1];
+                    }
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) { // DoubleByte
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                   // SingleByte
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            return dp;
+        }
+
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
+
         public int encodeChar(char ch) {
             return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
         }
@@ -741,9 +836,11 @@
     }
 
     public static class Encoder_DBCSONLY extends Encoder {
+
         public Encoder_DBCSONLY(Charset cs, byte[] repl,
-                         char[] c2b, char[] c2bIndex) {
-            super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex);
+                                char[] c2b, char[] c2bIndex,
+                                boolean isASCIICompatible) {
+            super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex, isASCIICompatible);
         }
 
         public int encodeChar(char ch) {
@@ -754,8 +851,6 @@
         }
     }
 
-
-
     public static class Encoder_EBCDIC extends Encoder {
         static final int SBCS = 0;
         static final int DBCS = 1;
@@ -764,8 +859,9 @@
 
         protected int  currentState = SBCS;
 
-        public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) {
-            super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex);
+        public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex,
+                              boolean isASCIICompatible) {
+            super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex, isASCIICompatible);
         }
 
         protected void implReset() {
@@ -878,6 +974,7 @@
             }
         }
 
+        @Override
         public int encode(char[] src, int sp, int len, byte[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -917,12 +1014,88 @@
             }
             return dp;
         }
+
+        @Override
+        public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = (char)(src[sp++] & 0xff);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    // no surrogate pair in latin1 string
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1)
+                        dst[dp++] = repl[1];
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) {           // DoubleByte
+                    if (currentState == SBCS) {
+                        currentState = DBCS;
+                        dst[dp++] = SO;
+                    }
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                             // SingleByte
+                    if (currentState == DBCS) {
+                         currentState = SBCS;
+                         dst[dp++] = SI;
+                    }
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            if (currentState == DBCS) {
+                 currentState = SBCS;
+                 dst[dp++] = SI;
+            }
+            return dp;
+        }
+
+        @Override
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    if (Character.isHighSurrogate(c) && sp < sl &&
+                        Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
+                        sp++;
+                    }
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1)
+                        dst[dp++] = repl[1];
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) {           // DoubleByte
+                    if (currentState == SBCS) {
+                        currentState = DBCS;
+                        dst[dp++] = SO;
+                    }
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                             // SingleByte
+                    if (currentState == DBCS) {
+                         currentState = SBCS;
+                         dst[dp++] = SI;
+                    }
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            if (currentState == DBCS) {
+                 currentState = SBCS;
+                 dst[dp++] = SI;
+            }
+            return dp;
+        }
     }
 
     // EUC_SIMPLE
     public static class Encoder_EUC_SIM extends Encoder {
-        public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) {
-            super(cs, c2b, c2bIndex);
+        public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex,
+                               boolean isASCIICompatible) {
+            super(cs, c2b, c2bIndex, isASCIICompatible);
         }
     }
 
--- a/src/java.base/share/classes/sun/nio/cs/HKSCS.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/HKSCS.java	Mon Nov 09 20:37:05 2015 -0800
@@ -53,7 +53,7 @@
             // super(cs, 0.5f, 1.0f);
             // need to extends DoubleByte.Decoder so the
             // sun.io can use it. this implementation
-            super(cs, 0.5f, 1.0f, null, null, 0, 0);
+            super(cs, 0.5f, 1.0f, null, null, 0, 0, true);
             this.big5Dec = big5Dec;
             this.b2cBmp = b2cBmp;
             this.b2cSupp = b2cSupp;
@@ -239,7 +239,7 @@
                           char[][] c2bBmp,
                           char[][] c2bSupp)
         {
-            super(cs, null, null);
+            super(cs, null, null, true);
             this.big5Enc = big5Enc;
             this.c2bBmp = c2bBmp;
             this.c2bSupp = c2bSupp;
@@ -389,6 +389,33 @@
             return dp;
         }
 
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            int dl = dst.length;
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    if (!Character.isHighSurrogate(c) || sp == sl ||
+                        !Character.isLowSurrogate(StringUTF16.getChar(src,sp)) ||
+                        (bb = encodeSupp(Character.toCodePoint(c, StringUTF16.getChar(src, sp++))))
+                        == UNMAPPABLE_ENCODING) {
+                        dst[dp++] = repl[0];
+                        if (repl.length > 1)
+                            dst[dp++] = repl[1];
+                        continue;
+                    }
+                }
+                if (bb > MAX_SINGLEBYTE) { // DoubleByte
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                   // SingleByte
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            return dp;
+        }
 
         static char[] C2B_UNMAPPABLE = new char[0x100];
         static {
--- a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java	Mon Nov 09 20:37:05 2015 -0800
@@ -132,6 +132,10 @@
                 dst[dp++] = (char)(src[sp++] & 0xff);
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
     private static class Encoder extends CharsetEncoder
@@ -297,5 +301,9 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 }
--- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java	Mon Nov 09 20:37:05 2015 -0800
@@ -49,10 +49,18 @@
     public static final class Decoder extends CharsetDecoder
                                       implements ArrayDecoder {
         private final char[] b2c;
+        private final boolean isASCIICompatible;
 
         public Decoder(Charset cs, char[] b2c) {
             super(cs, 1.0f, 1.0f);
             this.b2c = b2c;
+            this.isASCIICompatible = false;
+        }
+
+        public Decoder(Charset cs, char[] b2c, boolean isASCIICompatible) {
+            super(cs, 1.0f, 1.0f);
+            this.b2c = b2c;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
         private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
@@ -116,6 +124,7 @@
             repl = newReplacement.charAt(0);
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             if (len > dst.length)
                 len = dst.length;
@@ -129,6 +138,11 @@
             }
             return dp;
         }
+
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
     }
 
     public static final class Encoder extends CharsetEncoder
@@ -136,11 +150,13 @@
         private Surrogate.Parser sgp;
         private final char[] c2b;
         private final char[] c2bIndex;
+        private final boolean isASCIICompatible;
 
-        public Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
+        public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) {
             super(cs, 1.0f, 1.0f);
             this.c2b = c2b;
             this.c2bIndex = c2bIndex;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
         public boolean canEncode(char c) {
@@ -252,6 +268,51 @@
             }
             return dp;
         }
+
+        @Override
+        public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + Math.min(len, dst.length);
+            while (sp < sl) {
+                char c = (char)(src[sp++] & 0xff);
+                int b = encode(c);
+                if (b == UNMAPPABLE_ENCODING) {
+                    dst[dp++] = repl;
+                } else {
+                    dst[dp++] = (byte)b;
+                }
+            }
+            return dp;
+        }
+
+        @Override
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + Math.min(len, dst.length);
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int b = encode(c);
+                if (b != UNMAPPABLE_ENCODING) {
+                    dst[dp++] = (byte)b;
+                    continue;
+                }
+                if (Character.isHighSurrogate(c) && sp < sl &&
+                    Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
+                    if (len > dst.length) {
+                        sl++;
+                        len--;
+                    }
+                    sp++;
+                }
+                dst[dp++] = repl;
+            }
+            return dp;
+        }
+
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
     }
 
     // init the c2b and c2bIndex tables from b2c.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/nio/cs/StringUTF16.java	Mon Nov 09 20:37:05 2015 -0800
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.cs;
+
+import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
+import static sun.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE;
+
+class StringUTF16 {
+
+    public static char getChar(byte[] val, int index) {
+        return unsafe.getChar(val,
+                              ARRAY_BYTE_BASE_OFFSET + ARRAY_BYTE_INDEX_SCALE * index * 2L);
+    }
+
+    private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+}
--- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java	Mon Nov 09 20:37:05 2015 -0800
@@ -146,6 +146,10 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
     private static class Encoder extends CharsetEncoder
@@ -259,6 +263,10 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
 }
--- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java	Mon Nov 09 20:37:05 2015 -0800
@@ -549,6 +549,10 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
     private static final class Encoder extends CharsetEncoder
@@ -742,5 +746,9 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 }
--- a/src/java.base/share/classes/sun/security/provider/ByteArrayAccess.java	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/classes/sun/security/provider/ByteArrayAccess.java	Mon Nov 09 20:37:05 2015 -0800
@@ -30,7 +30,7 @@
 
 import java.nio.ByteOrder;
 
-import sun.misc.Unsafe;
+import jdk.internal.misc.Unsafe;
 
 /**
  * Optimized methods for converting between byte[] and int[]/long[], both for
--- a/src/java.base/share/native/libjava/String.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libjava/String.c	Mon Nov 09 20:37:05 2015 -0800
@@ -31,3 +31,14 @@
 {
     return JVM_InternString(env, this);
 }
+
+JNIEXPORT jboolean JNICALL
+Java_java_lang_StringUTF16_isBigEndian(JNIEnv *env, jclass cls)
+{
+  unsigned int endianTest = 0xff000000;
+  if (((char*)(&endianTest))[0] != 0) {
+    return JNI_TRUE;
+  } else {
+    return JNI_FALSE;
+  }
+}
--- a/src/java.base/share/native/libjava/check_version.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libjava/check_version.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,10 +24,11 @@
  */
 
 #include "jni.h"
+#include "jni_util.h"
 #include "jvm.h"
 
 JNIEXPORT jint JNICALL
-JNI_OnLoad(JavaVM *vm, void *reserved)
+DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
 {
     jint vm_version = JVM_GetInterfaceVersion();
     if (vm_version != JVM_INTERFACE_VERSION) {
--- a/src/java.base/share/native/libjava/jio.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libjava/jio.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
 
 #include "jni.h"
 
+#ifndef STATIC_BUILD
 
 /* This is a temporary solution until we figure out how to let native
  * libraries use jio_* without linking with the VM.
@@ -63,3 +64,6 @@
 
     return len;
 }
+
+#endif
+
--- a/src/java.base/share/native/libjava/jni_util.h	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libjava/jni_util.h	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -389,6 +389,117 @@
 
 extern size_t getLastErrorString(char *buf, size_t len);
 extern int getErrorString(int err, char *buf, size_t len);
+
+#ifdef STATIC_BUILD
+/* Macros for handling declaration of static/dynamic
+ * JNI library Load/Unload functions
+ *
+ * Use DEF_JNI_On{Un}Load when you want a static and non-static entry points.
+ * Use DEF_STATIC_JNI_On{Un}Load when you only want a static one.
+ *
+ * LIBRARY_NAME must be set to the name of the library
+ */
+
+/* These three macros are needed to get proper concatenation of
+ * the LIBRARY_NAME
+ *
+ * NOTE: LIBRARY_NAME must be set for static builds.
+ */
+#define ADD_LIB_NAME3(name, lib) name ## lib
+#define ADD_LIB_NAME2(name, lib) ADD_LIB_NAME3(name, lib)
+#define ADD_LIB_NAME(entry) ADD_LIB_NAME2(entry, LIBRARY_NAME)
+
+#define DEF_JNI_OnLoad \
+ADD_LIB_NAME(JNI_OnLoad_)(JavaVM *vm, void *reserved) \
+{ \
+  jint JNICALL ADD_LIB_NAME(JNI_OnLoad_dynamic_)(JavaVM *vm, void *reserved); \
+  ADD_LIB_NAME(JNI_OnLoad_dynamic_)(vm, reserved); \
+  return JNI_VERSION_1_8; \
+} \
+jint JNICALL ADD_LIB_NAME(JNI_OnLoad_dynamic_)
+
+#define DEF_STATIC_JNI_OnLoad \
+JNIEXPORT jint JNICALL ADD_LIB_NAME(JNI_OnLoad_)(JavaVM *vm, void *reserved) { \
+    return JNI_VERSION_1_8; \
+}
+
+#define DEF_JNI_OnUnload \
+ADD_LIB_NAME(JNI_OnUnload_)(JavaVM *vm, void *reserved) \
+{ \
+  void JNICALL ADD_LIB_NAME(JNI_OnUnload_dynamic_)(JavaVM *vm, void *reserved); \
+  ADD_LIB_NAME(JNI_OnUnload_dynamic_)(vm, reserved); \
+} \
+void JNICALL ADD_LIB_NAME(JNI_OnUnload_dynamic_)
+
+#define DEF_STATIC_JNI_OnUnload \
+ADD_LIB_NAME(JNI_OnUnload_)
+
+#else
+
+#define DEF_JNI_OnLoad JNI_OnLoad
+#define DEF_STATIC_JNI_OnLoad
+#define DEF_JNI_OnUnload JNI_OnUnload
+#define DEF_STATIC_JNI_OnUnload
+#endif
+
+#ifdef STATIC_BUILD
+/* Macros for handling declaration of static/dynamic
+ * Agent library Load/Attach/Unload functions
+ *
+ * Use DEF_Agent_OnLoad, DEF_Agent_OnAttach or DEF_Agent_OnUnload
+ *     when you want both static and non-static entry points.
+ * Use DEF_STATIC_Agent_OnLoad, DEF_STATIC_Agent_OnAttach or
+ *     DEF_STATIC_Agent_OnUnload when you only want a static one.
+ *
+ * LIBRARY_NAME must be set to the name of the library for static builds.
+ */
+
+#define DEF_Agent_OnLoad \
+ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) \
+{ \
+  jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_)(JavaVM *vm, char *options, void *reserved); \
+  return ADD_LIB_NAME(Agent_OnLoad_dynamic_)(vm, options, reserved); \
+} \
+jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_)
+
+#define DEF_STATIC_Agent_OnLoad \
+JNIEXPORT jint JNICALL ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) { \
+    return JNI_FALSE; \
+}
+
+#define DEF_Agent_OnAttach \
+ADD_LIB_NAME(Agent_OnAttach_)(JavaVM *vm, char *options, void *reserved) \
+{ \
+  jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_)(JavaVM *vm, char *options, void *reserved); \
+  return ADD_LIB_NAME(Agent_OnAttach_dynamic_)(vm, options, reserved); \
+} \
+jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_)
+
+#define DEF_STATIC_Agent_OnAttach \
+JNIEXPORT jint JNICALL ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) { \
+    return JNI_FALSE; \
+}
+
+#define DEF_Agent_OnUnload \
+ADD_LIB_NAME(Agent_OnUnload_)(JavaVM *vm) \
+{ \
+  void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_)(JavaVM *vm); \
+  ADD_LIB_NAME(Agent_OnUnload_dynamic_)(vm); \
+} \
+void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_)
+
+#define DEF_STATIC_Agent_OnUnload \
+ADD_LIB_NAME(Agent_OnUnload_)
+
+#else
+#define DEF_Agent_OnLoad Agent_OnLoad
+#define DEF_Agent_OnAttach Agent_OnAttach
+#define DEF_Agent_OnUnload Agent_OnUnload
+#define DEF_STATIC_Agent_OnLoad
+#define DEF_STATIC_Agent_OnAttach
+#define DEF_STATIC_Agent_OnUnload
+#endif
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif /* __cplusplus */
--- a/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp	Mon Nov 09 20:37:05 2015 -0800
@@ -26,6 +26,7 @@
 #include <string.h>
 
 #include "jni.h"
+#include "jni_util.h"
 
 #include "endian.hpp"
 #include "imageDecompressor.hpp"
@@ -246,7 +247,7 @@
 }
 
 JNIEXPORT jint JNICALL
-JNI_OnLoad(JavaVM *vm, void *reserved) {
+DEF_JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env;
 
     if (vm->GetEnv((void**) &env, JNI_VERSION_1_2) != JNI_OK) {
@@ -646,6 +647,6 @@
     return module;
 }
 
-JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
+JNIEXPORT void JNICALL DEF_JNI_OnUnload(JavaVM *vm, void *reserved) {
     ImageDecompressor::image_decompressor_close();
 }
--- a/src/java.base/share/native/libnet/net_util.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libnet/net_util.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,7 +38,7 @@
 }
 
 JNIEXPORT jint JNICALL
-JNI_OnLoad(JavaVM *vm, void *reserved)
+DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
 {
     JNIEnv *env;
     jclass iCls;
--- a/src/java.base/share/native/libnio/nio_util.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libnio/nio_util.c	Mon Nov 09 20:37:05 2015 -0800
@@ -28,7 +28,7 @@
 #include "jni_util.h"
 
 JNIEXPORT jint JNICALL
-JNI_OnLoad(JavaVM *vm, void *reserved)
+DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
 {
     JNIEnv *env;
 
--- a/src/java.base/share/native/libverify/check_code.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libverify/check_code.c	Mon Nov 09 20:37:05 2015 -0800
@@ -86,6 +86,7 @@
 #include <stdlib.h>
 
 #include "jni.h"
+#include "jni_util.h"
 #include "jvm.h"
 #include "classfile_constants.h"
 #include "opcodes.in_out"
@@ -481,6 +482,11 @@
 static void print_formatted_methodname(context_type *context, int index);
 #endif
 
+/*
+ * Declare library specific JNI_Onload entry if static build
+ */
+DEF_STATIC_JNI_OnLoad
+
 void initialize_class_hash(context_type *context)
 {
     hash_table_type *class_hash = &(context->class_hash);
--- a/src/java.base/share/native/libzip/ZipFile.c	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/share/native/libzip/ZipFile.c	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -55,6 +55,12 @@
 static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ;
 static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE;
 
+
+/*
+ * Declare library specific JNI_Onload entry if static build
+ */
+DEF_STATIC_JNI_OnLoad
+
 JNIEXPORT void JNICALL
 Java_java_util_zip_ZipFile_initIDs(JNIEnv *env, jclass cls)
 {
--- a/src/java.base/unix/native/libjava/jlong_md.h	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.base/unix/native/libjava/jlong_md.h	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -65,11 +65,19 @@
 #define jlong_zero_init  ((jlong) 0L)
 
 #ifdef _LP64
-#define jlong_to_ptr(a) ((void*)(a))
-#define ptr_to_jlong(a) ((jlong)(a))
+  #ifndef jlong_to_ptr
+    #define jlong_to_ptr(a) ((void*)(a))
+  #endif
+  #ifndef ptr_to_jlong
+    #define ptr_to_jlong(a) ((jlong)(a))
+  #endif
 #else
-#define jlong_to_ptr(a) ((void*)(int)(a))
-#define ptr_to_jlong(a) ((jlong)(int)(a))
+  #ifndef jlong_to_ptr
+    #define jlong_to_ptr(a) ((void*)(int)(a))
+  #endif
+  #ifndef ptr_to_jlong
+    #define ptr_to_jlong(a) ((jlong)(int)(a))
+  #endif
 #endif
 
 #define jint_to_jlong(a)        ((jlong)(a))
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m	Mon Nov 09 20:37:05 2015 -0800
@@ -740,7 +740,7 @@
     JNF_COCOA_EXIT(env)
 }
 
-JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
+JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) {
     OSXAPP_SetJavaVM(vm);
 
     // We need to let Foundation know that this is a multithreaded application, if it isn't already.
--- a/src/java.desktop/macosx/native/libjawt/jawt.m	Mon Nov 09 18:27:43 2015 -0800
+++ b/src/java.desktop/macosx/native/libjawt/jawt.m	Mon Nov 09 20:37:05 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,12 +24,18 @@
  */
 
 #import <jawt.h>
+#import "jni_util.h"
 
 #import <jawt_md.h>
 
 #import "awt_DrawingSurface.h"
 
 /*
+ * Declare library specific JNI_Onload entry if static build</