1/*
2 * Copyright (C) 2014 The Android Open Source Project
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 */
16package android.hardware.camera2.marshal.impl;
17
18import android.hardware.camera2.marshal.Marshaler;
19import android.hardware.camera2.marshal.MarshalQueryable;
20import android.hardware.camera2.utils.TypeReference;
21import android.util.Log;
22
23import java.nio.ByteBuffer;
24import java.nio.charset.Charset;
25
26import static android.hardware.camera2.impl.CameraMetadataNative.*;
27
28/**
29 * Marshal {@link String} to/from {@link #TYPE_BYTE}.
30 */
31public class MarshalQueryableString implements MarshalQueryable<String> {
32
33    private static final String TAG = MarshalQueryableString.class.getSimpleName();
34    private static final boolean DEBUG = false;
35
36    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
37    private static final byte NUL = (byte)'\0'; // used as string terminator
38
39    private class MarshalerString extends Marshaler<String> {
40
41        protected MarshalerString(TypeReference<String> typeReference, int nativeType) {
42            super(MarshalQueryableString.this, typeReference, nativeType);
43        }
44
45        @Override
46        public void marshal(String value, ByteBuffer buffer) {
47            byte[] arr = value.getBytes(UTF8_CHARSET);
48
49            buffer.put(arr);
50            buffer.put(NUL); // metadata strings are NUL-terminated
51        }
52
53        @Override
54        public int calculateMarshalSize(String value) {
55            byte[] arr = value.getBytes(UTF8_CHARSET);
56
57            return arr.length + 1; // metadata strings are NUL-terminated
58        }
59
60        @Override
61        public String unmarshal(ByteBuffer buffer) {
62            buffer.mark(); // save the current position
63
64            boolean foundNull = false;
65            int stringLength = 0;
66            while (buffer.hasRemaining()) {
67                if (buffer.get() == NUL) {
68                    foundNull = true;
69                    break;
70                }
71
72                stringLength++;
73            }
74
75            if (DEBUG) {
76                Log.v(TAG,
77                        "unmarshal - scanned " + stringLength + " characters; found null? "
78                                + foundNull);
79            }
80
81            if (!foundNull) {
82                throw new UnsupportedOperationException("Strings must be null-terminated");
83            }
84
85            buffer.reset(); // go back to the previously marked position
86
87            byte[] strBytes = new byte[stringLength + 1];
88            buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
89
90            // not including null character
91            return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET);
92        }
93
94        @Override
95        public int getNativeSize() {
96            return NATIVE_SIZE_DYNAMIC;
97        }
98    }
99
100    @Override
101    public Marshaler<String> createMarshaler(
102            TypeReference<String> managedType, int nativeType) {
103        return new MarshalerString(managedType, nativeType);
104    }
105
106    @Override
107    public boolean isTypeMappingSupported(TypeReference<String> managedType, int nativeType) {
108        return nativeType == TYPE_BYTE && String.class.equals(managedType.getType());
109    }
110}
111