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 class PreloadHolder {
37        public static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
38    }
39    private static final byte NUL = (byte)'\0'; // used as string terminator
40
41    private class MarshalerString extends Marshaler<String> {
42
43        protected MarshalerString(TypeReference<String> typeReference, int nativeType) {
44            super(MarshalQueryableString.this, typeReference, nativeType);
45        }
46
47        @Override
48        public void marshal(String value, ByteBuffer buffer) {
49            byte[] arr = value.getBytes(PreloadHolder.UTF8_CHARSET);
50
51            buffer.put(arr);
52            buffer.put(NUL); // metadata strings are NUL-terminated
53        }
54
55        @Override
56        public int calculateMarshalSize(String value) {
57            byte[] arr = value.getBytes(PreloadHolder.UTF8_CHARSET);
58
59            return arr.length + 1; // metadata strings are NUL-terminated
60        }
61
62        @Override
63        public String unmarshal(ByteBuffer buffer) {
64            buffer.mark(); // save the current position
65
66            boolean foundNull = false;
67            int stringLength = 0;
68            while (buffer.hasRemaining()) {
69                if (buffer.get() == NUL) {
70                    foundNull = true;
71                    break;
72                }
73
74                stringLength++;
75            }
76
77            if (DEBUG) {
78                Log.v(TAG,
79                        "unmarshal - scanned " + stringLength + " characters; found null? "
80                                + foundNull);
81            }
82
83            if (!foundNull) {
84                throw new UnsupportedOperationException("Strings must be null-terminated");
85            }
86
87            buffer.reset(); // go back to the previously marked position
88
89            byte[] strBytes = new byte[stringLength + 1];
90            buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
91
92            // not including null character
93            return new String(strBytes, /*offset*/0, stringLength, PreloadHolder.UTF8_CHARSET);
94        }
95
96        @Override
97        public int getNativeSize() {
98            return NATIVE_SIZE_DYNAMIC;
99        }
100    }
101
102    @Override
103    public Marshaler<String> createMarshaler(
104            TypeReference<String> managedType, int nativeType) {
105        return new MarshalerString(managedType, nativeType);
106    }
107
108    @Override
109    public boolean isTypeMappingSupported(TypeReference<String> managedType, int nativeType) {
110        return nativeType == TYPE_BYTE && String.class.equals(managedType.getType());
111    }
112}
113