Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 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 | |
| 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 |
| 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 |
| 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 |
| 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 | } |