1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.flatbuffers;
18 
19 import java.nio.ByteBuffer;
20 import java.nio.CharBuffer;
21 import java.nio.charset.CharacterCodingException;
22 import java.nio.charset.CharsetDecoder;
23 import java.nio.charset.CharsetEncoder;
24 import java.nio.charset.CoderResult;
25 import java.nio.charset.StandardCharsets;
26 
27 /**
28  * This class implements the Utf8 API using the Java Utf8 encoder. Use
29  * Utf8.setDefault(new Utf8Old()); to use it.
30  */
31 public class Utf8Old extends Utf8 {
32 
33   private static class Cache {
34     final CharsetEncoder encoder;
35     final CharsetDecoder decoder;
36     CharSequence lastInput = null;
37     ByteBuffer lastOutput = null;
38 
Cache()39     Cache() {
40       encoder = StandardCharsets.UTF_8.newEncoder();
41       decoder = StandardCharsets.UTF_8.newDecoder();
42     }
43   }
44 
45   private static final ThreadLocal<Cache> CACHE =
46       ThreadLocal.withInitial(() -> new Cache());
47 
48   // Play some games so that the old encoder doesn't pay twice for computing
49   // the length of the encoded string.
50 
51   @Override
encodedLength(CharSequence in)52   public int encodedLength(CharSequence in) {
53     final Cache cache = CACHE.get();
54     int estimated = (int) (in.length() * cache.encoder.maxBytesPerChar());
55     if (cache.lastOutput == null || cache.lastOutput.capacity() < estimated) {
56       cache.lastOutput = ByteBuffer.allocate(Math.max(128, estimated));
57     }
58     cache.lastOutput.clear();
59     cache.lastInput = in;
60     CharBuffer wrap = (in instanceof CharBuffer) ?
61                           (CharBuffer) in : CharBuffer.wrap(in);
62     CoderResult result = cache.encoder.encode(wrap, cache.lastOutput, true);
63     if (result.isError()) {
64       try {
65         result.throwException();
66       } catch (CharacterCodingException e) {
67         throw new IllegalArgumentException("bad character encoding", e);
68       }
69     }
70     cache.lastOutput.flip();
71     return cache.lastOutput.remaining();
72   }
73 
74   @Override
encodeUtf8(CharSequence in, ByteBuffer out)75   public void encodeUtf8(CharSequence in, ByteBuffer out) {
76     final Cache cache = CACHE.get();
77     if (cache.lastInput != in) {
78       // Update the lastOutput to match our input, although flatbuffer should
79       // never take this branch.
80       encodedLength(in);
81     }
82     out.put(cache.lastOutput);
83   }
84 
85   @Override
decodeUtf8(ByteBuffer buffer, int offset, int length)86   public String decodeUtf8(ByteBuffer buffer, int offset, int length) {
87     CharsetDecoder decoder = CACHE.get().decoder;
88     decoder.reset();
89     buffer = buffer.duplicate();
90     buffer.position(offset);
91     buffer.limit(offset + length);
92     try {
93       CharBuffer result = decoder.decode(buffer);
94       return result.toString();
95     } catch (CharacterCodingException e) {
96       throw new IllegalArgumentException("Bad encoding", e);
97     }
98   }
99 }
100