1/*
2 * Copyright (C) 2012 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 */
16
17package com.android.mms.util;
18
19import android.content.Context;
20
21import java.io.IOException;
22import java.nio.ByteBuffer;
23
24public class ImageCacheService {
25    @SuppressWarnings("unused")
26    private static final String TAG = "ImageCacheService";
27
28    public static final String IMAGE_CACHE_FILE = "imgcache";
29    private static final int IMAGE_CACHE_MAX_ENTRIES = 500;
30    private static final int IMAGE_CACHE_MAX_BYTES = 20 * 1024 * 1024;
31    private static final int IMAGE_CACHE_VERSION = 3;
32
33    private BlobCache mCache;
34
35    private static long[] sCrcTable = new long[256];
36    private static final long POLY64REV = 0x95AC9329AC4BC9B5L;
37    private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL;
38
39    private Context mContext;
40
41    public ImageCacheService(Context context) {
42        mCache = CacheManager.getCache(context, IMAGE_CACHE_FILE,
43                IMAGE_CACHE_MAX_ENTRIES, IMAGE_CACHE_MAX_BYTES,
44                IMAGE_CACHE_VERSION);
45        mContext = context;
46    }
47
48    public static class ImageData {
49        public ImageData(byte[] data, int offset) {
50            mData = data;
51            mOffset = offset;
52        }
53        public byte[] mData;
54        public int mOffset;
55    }
56
57    public ImageData getImageData(String path, int type) {
58        byte[] key = makeKey(path, type);
59        long cacheKey = crc64Long(key);
60        try {
61            byte[] value = null;
62            synchronized (mCache) {
63                value = mCache.lookup(cacheKey);
64            }
65            if (value == null) return null;
66            if (isSameKey(key, value)) {
67                int offset = key.length;
68                return new ImageData(value, offset);
69            }
70        } catch (IOException ex) {
71            // ignore.
72        }
73        return null;
74    }
75
76    public void putImageData(String path, int type, byte[] value) {
77        byte[] key = makeKey(path, type);
78        long cacheKey = crc64Long(key);
79        ByteBuffer buffer = ByteBuffer.allocate(key.length + value.length);
80        buffer.put(key);
81        buffer.put(value);
82        synchronized (mCache) {
83            try {
84                mCache.insert(cacheKey, buffer.array());
85            } catch (IOException ex) {
86                // ignore.
87            }
88        }
89    }
90
91    public void clear() {
92        CacheManager.clear(mContext);
93    }
94
95    private static byte[] makeKey(String path, int type) {
96        return getBytes(path + "+" + type);
97    }
98
99    private static boolean isSameKey(byte[] key, byte[] buffer) {
100        int n = key.length;
101        if (buffer.length < n) {
102            return false;
103        }
104        for (int i = 0; i < n; ++i) {
105            if (key[i] != buffer[i]) {
106                return false;
107            }
108        }
109        return true;
110    }
111
112    /**
113     * A function thats returns a 64-bit crc for string
114     *
115     * @param in input string
116     * @return a 64-bit crc value
117     */
118    public static final long crc64Long(String in) {
119        if (in == null || in.length() == 0) {
120            return 0;
121        }
122        return crc64Long(getBytes(in));
123    }
124
125    static {
126        // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c
127        long part;
128        for (int i = 0; i < 256; i++) {
129            part = i;
130            for (int j = 0; j < 8; j++) {
131                long x = ((int) part & 1) != 0 ? POLY64REV : 0;
132                part = (part >> 1) ^ x;
133            }
134            sCrcTable[i] = part;
135        }
136    }
137
138    public static final long crc64Long(byte[] buffer) {
139        long crc = INITIALCRC;
140        for (int k = 0, n = buffer.length; k < n; ++k) {
141            crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8);
142        }
143        return crc;
144    }
145
146    public static byte[] getBytes(String in) {
147        byte[] result = new byte[in.length() * 2];
148        int output = 0;
149        for (char ch : in.toCharArray()) {
150            result[output++] = (byte) (ch & 0xFF);
151            result[output++] = (byte) (ch >> 8);
152        }
153        return result;
154    }
155
156}
157