/**
 * END USER LICENSE AGREEMENT (“EULA”)
 *
 * READ THIS AGREEMENT CAREFULLY (date: 9/13/2011):
 * http://www.akiban.com/licensing/20110913
 *
 * BY INSTALLING OR USING ALL OR ANY PORTION OF THE SOFTWARE, YOU ARE ACCEPTING
 * ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. YOU AGREE THAT THIS
 * AGREEMENT IS ENFORCEABLE LIKE ANY WRITTEN AGREEMENT SIGNED BY YOU.
 *
 * IF YOU HAVE PAID A LICENSE FEE FOR USE OF THE SOFTWARE AND DO NOT AGREE TO
 * THESE TERMS, YOU MAY RETURN THE SOFTWARE FOR A FULL REFUND PROVIDED YOU (A) DO
 * NOT USE THE SOFTWARE AND (B) RETURN THE SOFTWARE WITHIN THIRTY (30) DAYS OF
 * YOUR INITIAL PURCHASE.
 *
 * IF YOU WISH TO USE THE SOFTWARE AS AN EMPLOYEE, CONTRACTOR, OR AGENT OF A
 * CORPORATION, PARTNERSHIP OR SIMILAR ENTITY, THEN YOU MUST BE AUTHORIZED TO SIGN
 * FOR AND BIND THE ENTITY IN ORDER TO ACCEPT THE TERMS OF THIS AGREEMENT. THE
 * LICENSES GRANTED UNDER THIS AGREEMENT ARE EXPRESSLY CONDITIONED UPON ACCEPTANCE
 * BY SUCH AUTHORIZED PERSONNEL.
 *
 * IF YOU HAVE ENTERED INTO A SEPARATE WRITTEN LICENSE AGREEMENT WITH AKIBAN FOR
 * USE OF THE SOFTWARE, THE TERMS AND CONDITIONS OF SUCH OTHER AGREEMENT SHALL
 * PREVAIL OVER ANY CONFLICTING TERMS OR CONDITIONS IN THIS AGREEMENT.
 */

package com.akiban.server.encoding;

import com.akiban.server.error.AkibanInternalException;
import com.akiban.server.rowdata.FieldDef;

import java.io.UnsupportedEncodingException;

/** Single byte encoding. */
public class UTF8Encoder extends VariableWidthEncoding {

    public static final Encoding INSTANCE = new UTF8Encoder();

    private UTF8Encoder() {
    }

    @Override
    public int widthFromObject(final FieldDef fieldDef, final Object value) {
        int size = fieldDef.getPrefixSize();
        if (value != null) {
            String str;
            if (value instanceof byte[]) {
                try {
                    str = new String((byte[]) value, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new AkibanInternalException("while decoding binary", e);
                }
            }
            else {
                str = value.toString();
            }
            for (int i = 0; i < str.length(); i++) {
                int ch = str.charAt(i);
                // Assumes consumers want standard UTF8 (e.g. String, nio.charset), not modified
                if (ch <= '\u007F')
                    size += 1;
                else if (ch <= '\u07FF')
                    size += 2;
                else
                    size += 3;
            }
        }
        return size;
    }
}

@@ -1561,16 +1561,266 @@
 der();%0A%0A
+    // See https://tools.ietf.org/html/rfc3629%0A    private static final int MAX_1_BYTE = 0x007F;%0A    private static final int MAX_2_BYTE = 0x07FF;%0A    private static final int MAX_3_BYTE = 0xFFFF;%0A    private static final int MAX_4_BYTE = 0x10FFFF;%0A%0A
     priv
@@ -2631,16 +2631,18 @@
  %3C= 
-'%5Cu007F'
+MAX_1_BYTE
 )%0A  
@@ -2705,16 +2705,18 @@
  %3C= 
-'%5Cu07FF'
+MAX_2_BYTE
 )%0A  
@@ -2764,16 +2764,18 @@
     else
+ %7B
 %0A       
@@ -2791,18 +2791,751 @@
     
-size += 3;
+// codePointAt will return the same as charAt if not a high surrogate pair *or* not followed by low%0A                    int codePoint = str.codePointAt(i);%0A                    if (codePoint == ch) %7B%0A                        size += 3;%0A                    %7D else %7B%0A                        if (++i %3E= str.length())%0A                            throw new IllegalStateException(%22Got codePoint but missing low pair: %22 + str);%0A                        if (codePoint %3C= MAX_3_BYTE)%0A                            size += 3;%0A                        else %7B%0A                            assert codePoint %3C= MAX_4_BYTE : %22Illegal code point: %22 + codePoint;%0A                            size += 4;%0A                        %7D%0A                    %7D%0A                %7D
 %0A   

Handle surrogate pairs in UTF8Encoder