├── .gitignore ├── src └── main │ ├── java │ └── io │ │ └── openmessaging │ │ ├── io │ │ ├── AsyncIO_task.java │ │ ├── tests │ │ │ └── test_io.java │ │ ├── IOReaderThread.java │ │ ├── AsyncIO.java │ │ └── IOThread.java │ │ ├── QueueStore.java │ │ ├── utils │ │ ├── UnsafeUtils.java │ │ ├── backup.md │ │ ├── MessageB64Serialization.java │ │ └── ChromiumBase64.java │ │ ├── DefaultQueueStoreImpl.java │ │ └── DemoTester.java │ └── resources │ └── package.xml ├── LICENSE ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | data* 4 | *.iml 5 | *.jar 6 | *tmp* 7 | *PlayGround.java 8 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/io/AsyncIO_task.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.io; 2 | 3 | public class AsyncIO_task { 4 | final long global_offset; 5 | public AsyncIO_task(long which_chunk){ 6 | this.global_offset = which_chunk; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/io/tests/test_io.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.io.tests; 2 | 3 | import io.openmessaging.io.AsyncIO; 4 | 5 | public class test_io { 6 | public static void main(String args[]){ 7 | int queue_num_per_file[] = new int[8]; 8 | int batch_size[] = new int[8]; 9 | for(int i = 0 ; i < 8; i++){ 10 | queue_num_per_file[i] = 100; 11 | batch_size[i] = 10; 12 | } 13 | AsyncIO asyncIO = new AsyncIO("data", 8, queue_num_per_file, batch_size); 14 | asyncIO.start(); 15 | asyncIO.waitFinishIO(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/package.xml: -------------------------------------------------------------------------------- 1 | 5 | package 6 | 7 | dir 8 | 9 | true 10 | 11 | 12 | src/main/resources 13 | conf 14 | false 15 | 16 | 17 | 18 | 19 | lib 20 | runtime 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/QueueStore.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging; 2 | 3 | import java.util.Collection; 4 | 5 | public abstract class QueueStore { 6 | /** 7 | * 把一条消息写入一个队列; 8 | * 这个接口需要是线程安全的,也即评测程序会并发调用该接口进行put; 9 | * 每个queue中的内容,按发送顺序存储消息(可以理解为Java中的List),同时每个消息会有一个索引,索引从0开始; 10 | * 不同queue中的内容,相互独立,互不影响; 11 | * @param queueName 代表queue名字,如果是第一次put,则自动生产一个queue 12 | * @param message message,代表消息的内容,评测时内容会随机产生,大部分长度在64字节左右,会有少量消息在1k左右 13 | */ 14 | abstract void put(String queueName, byte[] message); 15 | 16 | /** 17 | * 从一个队列中读出一批消息,读出的消息要按照发送顺序来; 18 | * 这个接口需要是线程安全的,也即评测程序会并发调用该接口进行get; 19 | * 返回的Collection会被并发读,但不涉及写,因此只需要是线程读安全就可以了; 20 | * @param queueName 代表队列的名字 21 | * @param offset 代表消息的在这个队列中的起始消息索引 22 | * @param num 代表读取的消息的条数,如果消息足够,则返回num条,否则只返回已有的消息即可;没有消息了,则返回一个空的集合 23 | */ 24 | abstract Collection get(String queueName, long offset, long num); 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lipeng WANG and Yulin CHE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/io/IOReaderThread.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.io; 2 | 3 | import io.openmessaging.DefaultQueueStoreImpl; 4 | 5 | import java.io.IOException; 6 | import java.nio.MappedByteBuffer; 7 | import java.nio.channels.FileChannel; 8 | 9 | public class IOReaderThread { 10 | final public static long MAPPED_BLOCK_SIZE = AsyncIO.INDEX_ENTRY_SIZE * DefaultQueueStoreImpl.CLUSTER_SIZE * 1024 * 4 * 50; 11 | final static long IO_READER_MAX_MAPED_CHUNK_NUM = (30L * 1024 * 1024 * 1024 / MAPPED_BLOCK_SIZE); 12 | 13 | IOReaderThread(FileChannel fd) { 14 | try { 15 | for (int i = 0; i < IO_READER_MAX_MAPED_CHUNK_NUM; i++) { 16 | if (i * MAPPED_BLOCK_SIZE < fd.size()) { 17 | long real_map_size = Math.min(MAPPED_BLOCK_SIZE, fd.size() - i * MAPPED_BLOCK_SIZE); 18 | index_mapped_buffers[i] = fd.map(FileChannel.MapMode.READ_ONLY, ((long) i) * MAPPED_BLOCK_SIZE, real_map_size); 19 | } else { 20 | break; 21 | } 22 | } 23 | } catch (IOException e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | public MappedByteBuffer index_mapped_buffers[] = new MappedByteBuffer[(int) IO_READER_MAX_MAPED_CHUNK_NUM]; 28 | } 29 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.openmessaging 8 | race2018 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.slf4j 15 | slf4j-log4j12 16 | 1.7.6 17 | 18 | 19 | 20 | 21 | 22 | 23 | maven-compiler-plugin 24 | 2.3.2 25 | 26 | 1.8 27 | 1.8 28 | utf-8 29 | 30 | 31 | 32 | maven-assembly-plugin 33 | 2.2.1 34 | 35 | 36 | org.apache.maven 37 | maven-core 38 | 2.2.1 39 | 40 | 41 | 42 | Race2018 43 | false 44 | 45 | src/main/resources/package.xml 46 | 47 | 48 | 49 | 50 | make-assembly 51 | package 52 | 53 | single 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Disclaimer 2 | 3 | 此项目仅供学习与交流使用,请遵循MIT协议,如果您在任何项目中使用相关代码,请保留此项目的LICENSE文件。 4 | 5 | >写在前面: 6 | > 1. 在开始coding前请仔细阅读以下内容 7 | 8 | 这个是Java Demo, C++ Demo 请查看[链接](https://code.aliyun.com/middlewarerace2018/queue-race-2018-cpp)。 9 | 提交的时候,代码地址只需要写":"后面的内容,也即省去"git@code.aliyun.com"。 10 | CPP的提交,请在镜像地址里写"CPP",Java的无需填写。 11 | 用户名和密码可以不用填写。 12 | 13 | ``` 14 | -Xms1G -Xmx1G -XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails 15 | ``` 16 | 17 | 18 | ## 1. 题目背景 19 | Apache RocketMQ作为的一款分布式的消息中间件,历年双十一承载了万亿级的消息流转,为业务方提供高性能低延迟的稳定可靠的消息服务。随着业务的逐步发展和云上的输出,单机队列数量的逐步增加,给RocketMQ带来了新的挑战。复赛的题目要求设计一个单机百万队列以上的存储引擎,单机内存有限,需要充分利用数据结构与存储技术,最大化吞吐量。 20 | 21 | ## 2. 题目描述 22 | 23 | ### 2.1 题目内容 24 | 实现一个进程内的队列引擎,单机可支持100万队列以上。 25 | 26 | 27 | ### 2.2 语言限定 28 | JAVA和C++ 29 | 30 | 31 | 注意: 32 | Java和C++一起参与排名。 33 | C++的Demo还在制作中(预计两天后可以出来),其核心逻辑与Java是一致的。选手们可以参考Java先行开始代码编写。 34 | 35 | 36 | ## 3. 程序目标 37 | 38 | 仔细阅读demo项目中的QueueStore,DefaultQueueStoreImpl,DemoTester三个类。 39 | 40 | 你的coding目标是重写DefaultQueueStoreImpl,并实现以下接口: 41 | abstract void put(String queueName, String message); 42 | abstract Collection get(String queueName, long offset, long num); 43 | 44 | 注: 45 | 评测时的数据存储路径为:/alidata1/race2018/data。 46 | 日志请直接打印在控制台标准输出,可以使用System.out.println,如果使用日志框架,请配置为ConsoleAppender。注意不要把日志输出到Error通道(也即不要使用System.err.println,如果使用日志框架,则不要使用log.error)。评测程序会把控制台标准输出的内容搜集出来,放置在OSS上面供用户排错,但是请不要密集打印日志,单次评测,最多不能超过100M。 47 | 日志下载路径为:http://race2018.oss-cn-beijing.aliyuncs.com/{teamcode}.logs.tgz,只保存最近一次任务的日志。 48 | 49 | 50 | ## 4.参赛方法说明 51 | 1. 在阿里天池找到"中间件性能挑战赛",并报名参加 52 | 2. 在code.aliyun.com注册一个账号,并新建一个仓库名,并将大赛官方账号middlewarerace2018添加为项目成员,权限为reporter 53 | 3. fork或者拷贝本仓库的代码到自己的仓库,并实现自己的逻辑 54 | 4. 在天池提交成绩的入口,提交自己的仓库git地址,等待评测结果 55 | 5. 坐等每天10点排名更新 56 | 57 | 58 | ## 4. 测试环境描述 59 | 测试环境为4c8g的ECS,限定使用的最大JVM大小为4GB(-Xmx4g)。带一块300G左右大小的SSD磁盘。 60 | 61 | SSD性能大致如下: 62 | iops 1w 左右;块读写能力(一次读写4K以上) 在200MB/s 左右。 63 | 64 | ulimit -a: 65 | 66 | ``` 67 | core file size (blocks, -c) 0 68 | data seg size (kbytes, -d) unlimited 69 | scheduling priority (-e) 0 70 | file size (blocks, -f) unlimited 71 | pending signals (-i) 31404 72 | max locked memory (kbytes, -l) 64 73 | max memory size (kbytes, -m) unlimited 74 | open files (-n) 6553560 75 | pipe size (512 bytes, -p) 8 76 | POSIX message queues (bytes, -q) 819200 77 | real-time priority (-r) 0 78 | stack size (kbytes, -s) 10240 79 | cpu time (seconds, -t) unlimited 80 | max user processes (-u) 31404 81 | virtual memory (kbytes, -v) unlimited 82 | file locks (-x) unlimited 83 | ``` 84 | 磁盘调度算法是 deadline 85 | 其它系统参数都是默认的。 86 | 87 | ## 5. 程序校验逻辑 88 | 89 | 校验程序分为三个阶段: 90 | 1.发送阶段 91 | 2.索引校验阶段 92 | 3.顺序消费阶段 93 | 请详细阅读DemoTester以理解评测程序的逻辑。 94 | 95 | ### 5.1. 程序校验规模说明 96 | 1.各个阶段线程数在20~30左右 97 | 2.发送阶段:消息大小在50字节左右,消息条数在20亿条左右,也即发送总数据在100G左右 98 | 3.索引校验阶段:会对所有队列的索引进行随机校验;平均每个队列会校验1~2次; 99 | 4.顺序消费阶段:挑选20%的队列进行全部读取和校验; 100 | 5.发送阶段最大耗时不能超过1800s;索引校验阶段和顺序消费阶段加在一起,最大耗时也不能超过1800s;超时会被判断为评测失败。 101 | 102 | ## 6. 排名规则 103 | 104 | 在结果校验100%正确的前提下,按照平均tps从高到低来排名 105 | 106 | 107 | ## 7. 第二/三方库规约 108 | 109 | * 仅允许依赖JavaSE 8 包含的lib 110 | * 可以参考别人的实现,拷贝少量的代码 111 | * 我们会对排名靠前的代码进行review,如果发现大量拷贝别人的代码,将扣分 112 | 113 | ## 8.作弊说明 114 | 115 | 所有消息都应该进行按实际发送的信息进行存储,可以压缩,但不能伪造。 116 | 如果发现有作弊行为,比如通过hack评测程序,绕过了必须的评测逻辑,则程序无效,且取消参赛资格。 117 | 118 | 119 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/utils/UnsafeUtils.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.utils; 2 | 3 | import sun.misc.Unsafe; 4 | import sun.nio.ch.DirectBuffer; 5 | 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Modifier; 8 | import java.nio.Buffer; 9 | import java.nio.ByteBuffer; 10 | import java.nio.MappedByteBuffer; 11 | 12 | // Not to be used in our project 13 | public class UnsafeUtils { 14 | private static Unsafe getUnsafe() { 15 | Unsafe unsafe = null; 16 | try { 17 | Field f = Unsafe.class.getDeclaredField("theUnsafe"); 18 | f.setAccessible(true); 19 | unsafe = (Unsafe) f.get(null); 20 | } catch (Exception e) { 21 | } 22 | return unsafe; 23 | } 24 | 25 | public static void main(String[] args) { 26 | Unsafe unsafe = getUnsafe(); 27 | long address = unsafe.allocateMemory(2); 28 | short number = 1; 29 | unsafe.putShort(address, number); 30 | if (unsafe.getByte(address) == 0) 31 | System.out.println("Big Endian"); 32 | else 33 | System.out.println("Little Endian"); 34 | unsafe.freeMemory(address); 35 | } 36 | 37 | //===================start of the unsafe hack======================== 38 | //https://stackoverflow.com/questions/15409727/missing-some-absolute-methods-on-bytebuffer 39 | private static final sun.misc.Unsafe UNSAFE; 40 | 41 | public static void unmap(MappedByteBuffer buffer) { 42 | if(buffer != null) { 43 | sun.misc.Cleaner cleaner = ((DirectBuffer) buffer).cleaner(); 44 | cleaner.clean(); 45 | } 46 | } 47 | 48 | static { 49 | Object result = null; 50 | try { 51 | Class klass = Class.forName("sun.misc.Unsafe"); 52 | for (Field field : klass.getDeclaredFields()) { 53 | if (field.getType() == klass && 54 | (field.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) == 55 | (Modifier.FINAL | Modifier.STATIC)) { 56 | field.setAccessible(true); 57 | result = field.get(null); 58 | break; 59 | } 60 | } 61 | } catch (Throwable ignored) { 62 | } 63 | UNSAFE = result == null ? null : (sun.misc.Unsafe) result; 64 | } 65 | 66 | private static final Field ADDRESS_FIELD; 67 | 68 | static { 69 | Field f; 70 | try { 71 | f = Buffer.class.getDeclaredField("address"); 72 | f.setAccessible(true); 73 | } catch (NoSuchFieldException | SecurityException e) { 74 | f = null; 75 | } 76 | ADDRESS_FIELD = f; 77 | } 78 | 79 | 80 | public static void absolutePut(ByteBuffer dstBuffer, int dstPosition, ByteBuffer srcBuffer) { 81 | if (!srcBuffer.isDirect()) { 82 | absolutePut(dstBuffer, dstPosition, 83 | srcBuffer.array(), srcBuffer.arrayOffset() + srcBuffer.position(), 84 | srcBuffer.remaining()); 85 | return; 86 | } 87 | 88 | if (UNSAFE != null && ADDRESS_FIELD != null && dstBuffer.isDirect()) { 89 | try { 90 | long dstAddress = (long) ADDRESS_FIELD.get(dstBuffer) + dstPosition; 91 | long srcAddress = (long) ADDRESS_FIELD.get(srcBuffer) + srcBuffer.position(); 92 | UNSAFE.copyMemory(srcAddress, dstAddress, srcBuffer.remaining()); 93 | } catch (IllegalAccessException e) { 94 | throw new RuntimeException(e); 95 | } 96 | } else { 97 | // fallback to basic loop 98 | for (int i = srcBuffer.position(); i < srcBuffer.limit(); i++) { 99 | dstBuffer.put(dstPosition + i, srcBuffer.get(i)); 100 | } 101 | } 102 | } 103 | 104 | public static void absolutePut(ByteBuffer dstBuffer, int dstPosition, byte[] src, int srcOffset, int length) { 105 | if (UNSAFE != null && ADDRESS_FIELD != null && dstBuffer.isDirect()) { 106 | try { 107 | long dstAddress = (long) ADDRESS_FIELD.get(dstBuffer) + dstPosition; 108 | UNSAFE.copyMemory( 109 | src, UNSAFE.arrayBaseOffset(byte[].class) + srcOffset, 110 | null, dstAddress, 111 | length); 112 | } catch (IllegalAccessException e) { 113 | throw new RuntimeException(e); 114 | } 115 | } else { 116 | // fallback to System.arraycopy 117 | System.arraycopy( 118 | src, srcOffset, 119 | dstBuffer.array(), dstBuffer.arrayOffset() + dstPosition, 120 | length); 121 | } 122 | } 123 | //===================end of the unsafe hack======================== 124 | } -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/io/AsyncIO.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.io; 2 | 3 | import io.openmessaging.DefaultQueueStoreImpl; 4 | 5 | import java.util.concurrent.BrokenBarrierException; 6 | import java.util.concurrent.ConcurrentSkipListSet; 7 | import java.util.concurrent.CyclicBarrier; 8 | import java.util.concurrent.locks.Lock; 9 | import java.util.concurrent.locks.ReentrantLock; 10 | 11 | enum IO_thread_status {INITING, RUNNING, CLOSING}; 12 | 13 | public class AsyncIO { 14 | final public static byte LARGE_MESSAGE_MAGIC_CHAR = '!'; 15 | final public static int INDEX_ENTRY_SIZE = 43; 16 | final public static int RAW_NORMAL_MESSAGE_SIZE = 58; 17 | final static int MAX_CONCURRENT_INDEX_MAPPED_BLOCK_NUM = 3; 18 | final static int MAX_BLOCKING_IO_TASK = 64; 19 | final public static long INDEX_MAPPED_BLOCK_SIZE = (INDEX_ENTRY_SIZE * 1000 * 250 * 10); 20 | final public static long INDEX_BLOCK_WRITE_TIMES_TO_FULL = (INDEX_MAPPED_BLOCK_SIZE / INDEX_ENTRY_SIZE); 21 | final public static int MAX_MAPED_CHUNK_NUM = (int) (24L * 1024 * 1024 * 1024 / INDEX_MAPPED_BLOCK_SIZE); 22 | 23 | static boolean mapped_buffer_flags = false; 24 | static boolean finished = false; 25 | static int finished_thread = 0; 26 | 27 | static boolean sended_flush_msg = false; 28 | static Lock send_lock = new ReentrantLock(); 29 | static int thread_num; 30 | public static IOThread work_threads[]; 31 | 32 | public static IOReaderThread ioReaderThread[] = new IOReaderThread[DefaultQueueStoreImpl.IO_THREAD]; 33 | 34 | public AsyncIO(String file_prefix, int thread_num, int queue_num_per_file[], int batch_size[]) { 35 | AsyncIO.thread_num = thread_num; 36 | this.work_threads = new IOThread[thread_num]; 37 | for (int i = 0; i < thread_num; i++) { 38 | work_threads[i] = new IOThread(i, file_prefix, queue_num_per_file[i], batch_size[i], MAX_BLOCKING_IO_TASK); 39 | } 40 | 41 | } 42 | 43 | public void start() { 44 | for (int i = 0; i < thread_num; i++) { 45 | this.work_threads[i].start(); 46 | } 47 | } 48 | 49 | public void submitIOTask(int which_thread, AsyncIO_task task) { 50 | if (work_threads[which_thread].status == IO_thread_status.RUNNING) { 51 | try { 52 | work_threads[which_thread].blockingQueue.put(task); 53 | } catch (InterruptedException e) { 54 | e.printStackTrace(); 55 | } 56 | } else { 57 | System.out.printf("failed to submit IO task to %d thread\n", which_thread); 58 | } 59 | } 60 | 61 | ConcurrentSkipListSet exitThreadSets = new ConcurrentSkipListSet<>(); 62 | final Object terminateLock = new Object(); 63 | 64 | public void waitFinishIO() { 65 | if (!finished) { 66 | send_lock.lock(); 67 | if (!sended_flush_msg) { 68 | sended_flush_msg = true; 69 | try { 70 | for (int i = 0; i < thread_num; i++) { 71 | AsyncIO_task task = new AsyncIO_task(-1); 72 | work_threads[i].blockingQueue.put(task); 73 | 74 | } 75 | } catch (InterruptedException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | send_lock.unlock(); 80 | 81 | synchronized (DefaultQueueStoreImpl.indexCheckerStartSync) { 82 | if (AsyncIO.finished_thread < thread_num) { 83 | try { 84 | DefaultQueueStoreImpl.indexCheckerStartSync.wait(); 85 | 86 | } catch (InterruptedException e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | } 91 | finished = true; 92 | } 93 | } 94 | 95 | private CyclicBarrier barrier1 = new CyclicBarrier(10, () -> { 96 | // for(int i = 0 ; i < DefaultQueueStoreImpl.IO_THREAD; i++){ 97 | // for(int chunkID = 0 ; chunkID < AsyncIO.MAX_MAPED_CHUNK_NUM; chunkID++){ 98 | // if(AsyncIO.work_threads[i].index_file_memory_blocks[chunkID] != null){ 99 | // AsyncIO.work_threads[i].index_file_memory_blocks[chunkID].clear(); 100 | // AsyncIO.work_threads[i].index_file_memory_blocks[chunkID] = null; 101 | // } 102 | // } 103 | // } 104 | for (int i = 0; i < DefaultQueueStoreImpl.IO_THREAD; i++) { 105 | ioReaderThread[i] = new IOReaderThread(AsyncIO.work_threads[i].index_file_fd); 106 | } 107 | }); 108 | 109 | public void waitMappedBuffers() { 110 | if (!mapped_buffer_flags) { 111 | try { 112 | barrier1.await(); 113 | } catch (InterruptedException | BrokenBarrierException e) { 114 | e.printStackTrace(); 115 | } 116 | mapped_buffer_flags = true; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/utils/backup.md: -------------------------------------------------------------------------------- 1 | ```java 2 | static int ChromiumBase64Decode(byte[] dest, byte[] src, int len) { 3 | // if (len == 0) return 0; 4 | // 5 | // /* 6 | // * if padding is used, then the message must be at least 7 | // * 4 chars and be a multiple of 4 8 | // */ 9 | // if (len < 4 || (len % 4 != 0)) { 10 | // return MODP_B64_ERROR; /* error */ 11 | // } 12 | // /* there can be at most 2 pad chars at the end */ 13 | // if (src[len - 1] == CHARPAD) { 14 | // len--; 15 | // if (src[len - 1] == CHARPAD) { 16 | // len--; 17 | // } 18 | // } 19 | 20 | int i; 21 | // int leftover = len % 4; 22 | // int chunks = (leftover == 0) ? len / 4 - 1 : len / 4; 23 | int chunks = len / 4; 24 | 25 | int x; 26 | int size_p = 0; 27 | 28 | int offset_y = 0; 29 | for (i = 0; i < chunks; ++i, offset_y += 4) { 30 | x = d0[src[offset_y]] | d1[src[1 + offset_y]] | d2[src[2 + offset_y]] | d3[src[3 + offset_y]]; 31 | // if (x >= BADCHAR) return MODP_B64_ERROR; 32 | dest[size_p++] = (byte) ((x >>> 0) & 0xff); //x[0] 33 | dest[size_p++] = (byte) ((x >>> 8) & 0xff); //x[1] 34 | dest[size_p++] = (byte) ((x >>> 16) & 0xff); //x[2] 35 | } 36 | 37 | // switch (leftover) { 38 | // case 0: 39 | // x = d0[src[offset_y]] | d1[src[offset_y + 1]] | d2[src[offset_y + 2]] | d3[src[offset_y + 3]]; 40 | // 41 | // if (x >= BADCHAR) return MODP_B64_ERROR; 42 | // dest[size_p++] = (byte) ((x >>> 0) & 0xff); //x[0] 43 | // dest[size_p++] = (byte) ((x >>> 8) & 0xff); //x[1] 44 | // dest[size_p] = (byte) ((x >>> 16) & 0xff); //x[2] 45 | // return (chunks + 1) * 3; 46 | // case 1: /* with padding this is an impossible case */ 47 | // x = d0[src[offset_y]]; 48 | // dest[size_p] = (byte) ((x >>> 0) & 0xff); //x[0]// i.e. first char/byte in int 49 | // break; 50 | // case 2: // * case 2, 1 output byte */ 51 | // x = d0[src[offset_y]] | d1[src[offset_y + 1]]; 52 | // dest[size_p] = (byte) ((x >>> 0) & 0xff); //x[0] // i.e. first char 53 | // break; 54 | // default: /* case 3, 2 output bytes */ 55 | // x = d0[src[offset_y]] | d1[src[offset_y + 1]] | d2[src[offset_y + 2]]; /* 0x3c */ 56 | // dest[size_p++] = (byte) ((x >>> 0) & 0xff); //x[0] 57 | // dest[size_p] = (byte) ((x >>> 8) & 0xff); //x[1] 58 | // break; 59 | // } 60 | // if (x >= BADCHAR) return MODP_B64_ERROR; 61 | // return 3 * chunks + (6 * leftover) / 8; 62 | return 3 * chunks; 63 | } 64 | ``` 65 | 66 | ```java 67 | static int ChromiumBase64Encode(byte[] dest, byte[] str, int len) { 68 | int i = 0; 69 | int size_p = 0; 70 | 71 | /* unsigned here is important! */ // for correct offset loopup 72 | byte t1, t2, t3; 73 | 74 | if (len > 2) { 75 | for (; i < len - 2; i += 3) { 76 | t1 = str[i]; 77 | t2 = str[i + 1]; 78 | t3 = str[i + 2]; 79 | dest[size_p++] = e0[t1 & 0xff]; 80 | dest[size_p++] = e1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) & 0xff]; 81 | dest[size_p++] = e1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) & 0xff]; 82 | dest[size_p++] = e2[t3 & 0xff]; 83 | } 84 | } 85 | 86 | switch (len - i) { 87 | case 0: 88 | break; 89 | case 1: 90 | t1 = str[i]; 91 | dest[size_p++] = e0[t1 & 0xff]; 92 | dest[size_p++] = e1[((t1 & 0x03) << 4) & 0xff]; 93 | dest[size_p++] = CHARPAD; 94 | dest[size_p++] = CHARPAD; 95 | break; 96 | default: /* case 2 */ 97 | t1 = str[i]; 98 | t2 = str[i + 1]; 99 | dest[size_p++] = e0[t1 & 0xff]; 100 | dest[size_p++] = e1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) & 0xff]; 101 | dest[size_p++] = e2[((t2 & 0x0F) << 2) & 0xff]; 102 | dest[size_p++] = CHARPAD; 103 | } 104 | // dest[size_p] = '\0'; // no need to use \0 in java 105 | return size_p; 106 | } 107 | ``` 108 | 109 | ```java 110 | 111 | public static int ChromiumBase6EncodeLen(int A) { 112 | return ((A + 2) / 3 * 4 + 1); // 2 for padding, 3 bytes -> 4 bytes, 1 is for '\0' 113 | } 114 | ``` 115 | 116 | ```java 117 | static int MODP_B64_ERROR = ((Integer.MAX_VALUE) - 1); 118 | static byte CHAR62 = '+'; 119 | static byte CHAR63 = '/'; 120 | private static int BADCHAR = 0x01FFFFFF; 121 | 122 | 123 | ``` 124 | 125 | ```java 126 | switch (len - i) { 127 | case 0: 128 | break; 129 | case 1: 130 | t1 = str[i]; 131 | dest[size_p++] = e0[t1 & 0xff]; 132 | dest[size_p++] = e1[((t1 & 0x03) << 4) & 0xff]; 133 | dest[size_p++] = CHARPAD; 134 | dest[size_p++] = CHARPAD; 135 | break; 136 | default: /* case 2 */ 137 | t1 = str[i]; 138 | t2 = str[i + 1]; 139 | dest[size_p++] = e0[t1 & 0xff]; 140 | dest[size_p++] = e1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) & 0xff]; 141 | dest[size_p++] = e2[((t2 & 0x0F) << 2) & 0xff]; 142 | dest[size_p++] = CHARPAD; 143 | } 144 | ``` 145 | 146 | ```java 147 | static int ChromiumBase64Encode(byte[] dest, byte[] str, int len) { 148 | int i = 0; 149 | int size_p = 0; 150 | /* unsigned here is important! */ // for correct offset loopup 151 | byte t1, t2, t3; 152 | if (len > 2) { 153 | for (; i < len - 2; i += 3) { 154 | t1 = str[i]; 155 | t2 = str[i + 1]; 156 | t3 = str[i + 2]; 157 | dest[size_p++] = e0[t1 & 0xff]; 158 | dest[size_p++] = e1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) & 0xff]; 159 | dest[size_p++] = e1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) & 0xff]; 160 | dest[size_p++] = e2[t3 & 0xff]; 161 | } 162 | } 163 | return size_p; 164 | } 165 | ``` -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/io/IOThread.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.io; 2 | 3 | import io.openmessaging.DefaultQueueStoreImpl; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.nio.ByteBuffer; 8 | import java.nio.MappedByteBuffer; 9 | import java.nio.channels.FileChannel; 10 | import java.nio.file.StandardOpenOption; 11 | import java.util.concurrent.ArrayBlockingQueue; 12 | import java.util.concurrent.BlockingQueue; 13 | import java.util.concurrent.atomic.AtomicIntegerArray; 14 | import java.util.concurrent.atomic.AtomicLong; 15 | 16 | import static io.openmessaging.utils.UnsafeUtils.absolutePut; 17 | import static io.openmessaging.utils.UnsafeUtils.unmap; 18 | 19 | public class IOThread extends Thread { 20 | 21 | private void doIO(AsyncIO_task task) throws IOException { 22 | for (; current_index_mapped_start_chunk < AsyncIO.MAX_MAPED_CHUNK_NUM; current_index_mapped_start_chunk++) { 23 | if (index_mapped_block_write_counter.get(current_index_mapped_start_chunk) >= AsyncIO.INDEX_BLOCK_WRITE_TIMES_TO_FULL) { 24 | 25 | // MappedByteBuffer index_file_buf = index_file_fd.map(FileChannel.MapMode.READ_WRITE, 26 | // AsyncIO.INDEX_MAPPED_BLOCK_SIZE * current_index_mapped_start_chunk, AsyncIO.INDEX_MAPPED_BLOCK_SIZE); 27 | // absolutePut(index_file_buf, 0, index_file_memory_blocks[current_index_mapped_start_chunk], 0, (int) AsyncIO.INDEX_MAPPED_BLOCK_SIZE); 28 | // unmap(index_file_buf); 29 | index_file_fd.write(ByteBuffer.wrap(index_file_memory_blocks[current_index_mapped_start_chunk]), AsyncIO.INDEX_MAPPED_BLOCK_SIZE * current_index_mapped_start_chunk); 30 | 31 | int next_chunk = current_index_mapped_start_chunk + AsyncIO.MAX_CONCURRENT_INDEX_MAPPED_BLOCK_NUM; 32 | current_index_mapped_start_offset += AsyncIO.INDEX_MAPPED_BLOCK_SIZE; 33 | current_index_mapped_end_offset += AsyncIO.INDEX_MAPPED_BLOCK_SIZE; 34 | this.index_file_size = this.current_index_mapped_end_offset; 35 | this.index_file_fd.truncate(this.index_file_size); 36 | 37 | index_file_memory_blocks[next_chunk] = index_file_memory_blocks[current_index_mapped_start_chunk]; 38 | index_file_memory_blocks[current_index_mapped_start_chunk] = null; 39 | synchronized (sync_blocks[next_chunk]) { 40 | sync_blocks[next_chunk].notifyAll(); 41 | } 42 | } else { 43 | break; 44 | } 45 | } 46 | } 47 | 48 | @Override 49 | public void run() { 50 | this.status = IO_thread_status.RUNNING; 51 | for (; ; ) { 52 | AsyncIO_task task = null; 53 | try { 54 | task = this.blockingQueue.take(); 55 | } catch (InterruptedException e) { 56 | e.printStackTrace(); 57 | } 58 | if (this.status == IO_thread_status.CLOSING || task.global_offset == -1) { 59 | for (int i = current_index_mapped_start_chunk; i < AsyncIO.MAX_MAPED_CHUNK_NUM && index_file_memory_blocks[i] != null; i++) { 60 | try { 61 | // MappedByteBuffer index_file_buf = index_file_fd.map(FileChannel.MapMode.READ_WRITE, i * AsyncIO.INDEX_MAPPED_BLOCK_SIZE, AsyncIO.INDEX_MAPPED_BLOCK_SIZE); 62 | // absolutePut(index_file_buf, 0, index_file_memory_blocks[i], 0, (int) AsyncIO.INDEX_MAPPED_BLOCK_SIZE); 63 | // unmap(index_file_buf); 64 | index_file_fd.write(ByteBuffer.wrap(index_file_memory_blocks[i]), AsyncIO.INDEX_MAPPED_BLOCK_SIZE * i); 65 | index_file_memory_blocks[i] = null; 66 | } catch (IOException e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | // must print out, otherwise incorrect because of compiler jit optmization 71 | synchronized (DefaultQueueStoreImpl.indexCheckerStartSync) { 72 | AsyncIO.finished_thread++; 73 | if (AsyncIO.finished_thread == DefaultQueueStoreImpl.IO_THREAD) { 74 | DefaultQueueStoreImpl.indexCheckerStartSync.notifyAll(); 75 | } 76 | } 77 | break; 78 | } 79 | try { 80 | doIO(task); 81 | } catch (IOException e) { 82 | e.printStackTrace(); 83 | } 84 | } 85 | } 86 | 87 | IOThread(int thread_id, String file_prefix, int queue_num_per_file, int batch_size, int blocking_queue_size) { 88 | this.status = IO_thread_status.INITING; 89 | this.batch_size = batch_size; 90 | this.queue_num_per_file = queue_num_per_file; 91 | this.thread_id = thread_id; 92 | this.chunk_size = queue_num_per_file * batch_size; 93 | File data_file_name_f = new File(file_prefix + '_' + thread_id + ".data"); 94 | File index_file_name_f = new File(file_prefix + '_' + thread_id + ".idx"); 95 | try { 96 | data_file_fd = FileChannel.open(data_file_name_f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, 97 | StandardOpenOption.CREATE, StandardOpenOption.SPARSE, StandardOpenOption.TRUNCATE_EXISTING); 98 | index_file_fd = FileChannel.open(index_file_name_f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, 99 | StandardOpenOption.CREATE, StandardOpenOption.SPARSE, StandardOpenOption.TRUNCATE_EXISTING); 100 | } catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | blockingQueue = new ArrayBlockingQueue<>(AsyncIO.MAX_BLOCKING_IO_TASK); 104 | this.queue_counter = new AtomicIntegerArray(queue_num_per_file); 105 | 106 | try { 107 | this.current_index_mapped_start_offset = 0; 108 | this.current_index_mapped_start_chunk = 0; 109 | this.current_index_mapped_end_offset = AsyncIO.MAX_CONCURRENT_INDEX_MAPPED_BLOCK_NUM * AsyncIO.INDEX_MAPPED_BLOCK_SIZE; 110 | this.index_file_size = this.current_index_mapped_end_offset; 111 | 112 | this.index_file_fd.truncate(this.index_file_size); 113 | for (int i = 0; i < AsyncIO.MAX_CONCURRENT_INDEX_MAPPED_BLOCK_NUM; i++) { 114 | index_file_memory_blocks[i] = new byte[(int) AsyncIO.INDEX_MAPPED_BLOCK_SIZE]; 115 | } 116 | 117 | } catch (IOException e) { 118 | e.printStackTrace(); 119 | } 120 | for (int i = 0; i < AsyncIO.MAX_MAPED_CHUNK_NUM; i++) { 121 | sync_blocks[i] = new Object(); 122 | } 123 | } 124 | 125 | private final int thread_id; 126 | final public long chunk_size; 127 | private final int queue_num_per_file; 128 | final public int batch_size; 129 | public AtomicLong data_file_size = new AtomicLong(0); 130 | public long index_file_size = 0; 131 | public AtomicIntegerArray queue_counter; 132 | 133 | public FileChannel data_file_fd; 134 | public FileChannel index_file_fd; 135 | final BlockingQueue blockingQueue; 136 | IO_thread_status status; 137 | 138 | public long current_index_mapped_start_offset = 0; 139 | public long current_index_mapped_end_offset = 0; 140 | public int current_index_mapped_start_chunk = 0; 141 | 142 | public byte[][] index_file_memory_blocks = new byte[AsyncIO.MAX_MAPED_CHUNK_NUM][]; 143 | public AtomicIntegerArray index_mapped_block_write_counter = new AtomicIntegerArray(AsyncIO.MAX_MAPED_CHUNK_NUM); 144 | public Object sync_blocks[] = new Object[AsyncIO.MAX_MAPED_CHUNK_NUM]; 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/DefaultQueueStoreImpl.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging; 2 | 3 | 4 | import io.openmessaging.io.AsyncIO; 5 | import io.openmessaging.io.AsyncIO_task; 6 | import io.openmessaging.io.IOReaderThread; 7 | import io.openmessaging.io.IOThread; 8 | import io.openmessaging.utils.MessageB64Serialization; 9 | 10 | import java.io.IOException; 11 | import java.nio.ByteBuffer; 12 | import java.nio.channels.FileChannel; 13 | import java.util.ArrayList; 14 | import java.util.Collection; 15 | import java.util.concurrent.ConcurrentSkipListSet; 16 | 17 | import static io.openmessaging.utils.UnsafeUtils.absolutePut; 18 | 19 | /** 20 | * 这是一个简单的基于内存的实现,以方便选手理解题意; 21 | * 实际提交时,请维持包名和类名不变,把方法实现修改为自己的内容; 22 | */ 23 | public class DefaultQueueStoreImpl extends QueueStore { 24 | final static String file_prefix = "/alidata1/race2018/data/data"; 25 | final static long TOTAL_QUEUE_NUM = 1000000; 26 | final public static int IO_THREAD = 4; 27 | final public static int CLUSTER_SIZE = 20; 28 | final static int queue_nums[] = new int[IO_THREAD]; 29 | final static int cluster_size[] = new int[IO_THREAD]; 30 | 31 | final private static FileChannel data_file_handles[] = new FileChannel[IO_THREAD]; 32 | final private static FileChannel index_file_handles[] = new FileChannel[IO_THREAD]; 33 | 34 | final private AsyncIO asyncIO; 35 | public static final Object indexCheckerStartSync = new Object(); 36 | 37 | private boolean phase3 = false; 38 | private static ConcurrentSkipListSet tidSet = new ConcurrentSkipListSet<>(); 39 | 40 | private static long getQueueID(String queueName) { 41 | long res = 0; 42 | long multiplier = 1; 43 | for (int i = queueName.length() - 1; i >= 0 && queueName.charAt(i) >= '0' && queueName.charAt(i) <= '9'; i--) { 44 | res += (queueName.charAt(i) - '0') * multiplier; 45 | multiplier *= 10; 46 | } 47 | return res; 48 | } 49 | 50 | public DefaultQueueStoreImpl() { 51 | int q_ave = (int) TOTAL_QUEUE_NUM / IO_THREAD; 52 | for (int i = 0; i < IO_THREAD; i++) { 53 | queue_nums[i] = q_ave; 54 | cluster_size[i] = CLUSTER_SIZE; 55 | } 56 | queue_nums[IO_THREAD - 1] = (int) TOTAL_QUEUE_NUM - q_ave * (IO_THREAD - 1); 57 | cluster_size[IO_THREAD - 1] = CLUSTER_SIZE; 58 | 59 | asyncIO = new AsyncIO(file_prefix, IO_THREAD, queue_nums, cluster_size); 60 | asyncIO.start(); 61 | 62 | for (int i = 0; i < IO_THREAD; i++) { 63 | data_file_handles[i] = AsyncIO.work_threads[i].data_file_fd; 64 | index_file_handles[i] = AsyncIO.work_threads[i].index_file_fd; 65 | } 66 | } 67 | 68 | public void put(String queueName, byte[] message) { 69 | long queueID = getQueueID(queueName); 70 | int threadID = (int) (queueID % IO_THREAD); 71 | 72 | IOThread ioThread = AsyncIO.work_threads[threadID]; 73 | 74 | long which_queue_in_this_io_thread = queueID / IO_THREAD; 75 | long queue_offset = ioThread.queue_counter.getAndIncrement((int) which_queue_in_this_io_thread); 76 | 77 | long chunk_id = ((queue_offset / ioThread.batch_size) * (ioThread.chunk_size) + 78 | (which_queue_in_this_io_thread * ioThread.batch_size) + queue_offset % ioThread.batch_size); 79 | 80 | long idx_file_offset = AsyncIO.INDEX_ENTRY_SIZE * chunk_id; 81 | int which_mapped_chunk = (int) (idx_file_offset / AsyncIO.INDEX_MAPPED_BLOCK_SIZE); 82 | 83 | long offset_in_mapped_chunk = idx_file_offset % AsyncIO.INDEX_MAPPED_BLOCK_SIZE; 84 | while (ioThread.index_file_memory_blocks[which_mapped_chunk] == null) { 85 | // System.out.println("I am full..."); 86 | synchronized (ioThread.sync_blocks[which_mapped_chunk]) { 87 | if (ioThread.index_file_memory_blocks[which_mapped_chunk] == null) { 88 | try { 89 | ioThread.sync_blocks[which_mapped_chunk].wait(); 90 | } catch (InterruptedException e) { 91 | e.printStackTrace(); 92 | } 93 | } 94 | } 95 | } 96 | 97 | if (message.length > AsyncIO.RAW_NORMAL_MESSAGE_SIZE) { 98 | ByteBuffer index_record = ByteBuffer.allocate(AsyncIO.INDEX_ENTRY_SIZE); 99 | byte[] message_ser_buf = MessageB64Serialization.SerializeBase64DecodingSkipIndex(message, message.length); 100 | long large_msg_size = message_ser_buf.length; 101 | long large_msg_offset = ioThread.data_file_size.getAndAdd(large_msg_size); 102 | 103 | index_record.putLong(4, large_msg_offset); 104 | index_record.putLong(12, large_msg_size); 105 | index_record.put(AsyncIO.INDEX_ENTRY_SIZE - 1, AsyncIO.LARGE_MESSAGE_MAGIC_CHAR); 106 | try { 107 | ioThread.data_file_fd.write(ByteBuffer.wrap(message_ser_buf), large_msg_offset); 108 | } catch (IOException e) { 109 | e.printStackTrace(); 110 | } 111 | for(int i = 0 ; i < AsyncIO.INDEX_ENTRY_SIZE; i++){ 112 | ioThread.index_file_memory_blocks[which_mapped_chunk][(int) (offset_in_mapped_chunk + i)] = index_record.get(i); 113 | } 114 | //absolutePut(ioThread.index_file_memory_blocks[which_mapped_chunk], (int) offset_in_mapped_chunk, index_record); 115 | } else { 116 | MessageB64Serialization.SerializeBase64DecodingSkipIndexOff(message, message.length, 117 | ioThread.index_file_memory_blocks[which_mapped_chunk], (int) offset_in_mapped_chunk); 118 | } 119 | 120 | int write_times = ioThread.index_mapped_block_write_counter.incrementAndGet(which_mapped_chunk); 121 | 122 | if (write_times == AsyncIO.INDEX_BLOCK_WRITE_TIMES_TO_FULL) { 123 | // System.out.println("send one remap task"); 124 | AsyncIO_task task = new AsyncIO_task(0); 125 | asyncIO.submitIOTask(threadID, task); 126 | } 127 | 128 | } 129 | 130 | static class MemoryPool { 131 | Collection results = new ArrayList<>((int) 48); 132 | byte[][] bytes = new byte[48][]; 133 | ByteBuffer byteBuffer = ByteBuffer.allocate(AsyncIO.INDEX_ENTRY_SIZE * CLUSTER_SIZE); 134 | 135 | MemoryPool() { 136 | for (int i = 0; i < 48; i++) { 137 | bytes[i] = new byte[58]; 138 | } 139 | } 140 | } 141 | 142 | private static MemoryPool threadMemoryPool[] = new MemoryPool[128]; 143 | 144 | static { 145 | for (int i = 0; i < threadMemoryPool.length; i++) { 146 | threadMemoryPool[i] = new MemoryPool(); 147 | } 148 | } 149 | 150 | public Collection get(String queueName, long offset, long num) { 151 | long currentTid = Thread.currentThread().getId(); 152 | if (!phase3) { 153 | asyncIO.waitFinishIO(); 154 | asyncIO.waitMappedBuffers(); 155 | if (!tidSet.contains(currentTid)) { 156 | tidSet.add(currentTid); 157 | if (tidSet.size() > 10) { 158 | phase3 = true; 159 | System.out.println("start check phase"); 160 | } 161 | } 162 | } 163 | 164 | MemoryPool memoryStruct = threadMemoryPool[(int) currentTid]; 165 | 166 | 167 | Collection results = memoryStruct.results; 168 | results.clear(); 169 | 170 | long queueID = getQueueID(queueName); 171 | 172 | int threadID = (int) (queueID % IO_THREAD); 173 | IOThread ioThread = AsyncIO.work_threads[threadID]; 174 | int batch_size = ioThread.batch_size; 175 | int which_queue_in_this_io_thread = (int) (queueID / IO_THREAD); 176 | 177 | long queue_count = ioThread.queue_counter.get(which_queue_in_this_io_thread); 178 | long max_offset = Math.min(offset + num, queue_count); 179 | 180 | IOReaderThread ioReaderThread = AsyncIO.ioReaderThread[threadID]; 181 | 182 | ByteBuffer index_record = memoryStruct.byteBuffer; 183 | 184 | int result_counter = 0; 185 | 186 | long chunk_size = ioThread.chunk_size; 187 | long chunk_offset = (which_queue_in_this_io_thread * batch_size); 188 | for (long queue_offset = offset; queue_offset < max_offset; ) { 189 | long left_num = batch_size - (queue_offset % batch_size); 190 | if (queue_offset + left_num > max_offset) { 191 | left_num = max_offset - queue_offset; 192 | } 193 | long chunk_id = ((queue_offset / batch_size) * chunk_size + chunk_offset + queue_offset % batch_size); 194 | long idx_file_offset = AsyncIO.INDEX_ENTRY_SIZE * chunk_id; 195 | 196 | index_record.position(0); 197 | index_record.limit((int) (left_num * AsyncIO.INDEX_ENTRY_SIZE)); 198 | 199 | if (phase3) { 200 | 201 | int which_chunk_in_io_file = (int) (idx_file_offset / IOReaderThread.MAPPED_BLOCK_SIZE); 202 | 203 | int index_in_this_mapped_chunk = (int) (idx_file_offset % IOReaderThread.MAPPED_BLOCK_SIZE); 204 | for (int i = 0; i < index_record.limit(); i++) { 205 | index_record.put(i, ioReaderThread.index_mapped_buffers[which_chunk_in_io_file].get(index_in_this_mapped_chunk + i)); 206 | } 207 | } else { 208 | try { 209 | ioThread.index_file_fd.read(index_record, idx_file_offset); 210 | } catch (IOException e) { 211 | e.printStackTrace(); 212 | } 213 | } 214 | 215 | for (int element = 0; element < left_num; element++) { 216 | byte output_buf[]; 217 | if (index_record.get(AsyncIO.INDEX_ENTRY_SIZE * (element + 1) - 1) == AsyncIO.LARGE_MESSAGE_MAGIC_CHAR) { 218 | long large_msg_size; 219 | long large_msg_offset; 220 | large_msg_offset = index_record.getLong(4 + AsyncIO.INDEX_ENTRY_SIZE * element); 221 | large_msg_size = index_record.getLong(12 + AsyncIO.INDEX_ENTRY_SIZE * element); 222 | ByteBuffer large_msg_buf = ByteBuffer.allocate((int) large_msg_size); 223 | try { 224 | ioThread.data_file_fd.read(large_msg_buf, large_msg_offset); 225 | } catch (IOException e) { 226 | e.printStackTrace(); 227 | } 228 | output_buf = MessageB64Serialization.DeserializeBase64EncodingAddIndex(large_msg_buf.array(), 229 | 0, large_msg_buf.position(), (int) (queue_offset + element)); 230 | results.add(output_buf); 231 | } else { 232 | 233 | MessageB64Serialization.DeserializeBase64EncodingAddIndexNormalSize(index_record.array(), 234 | element * AsyncIO.INDEX_ENTRY_SIZE, AsyncIO.INDEX_ENTRY_SIZE, (int) (queue_offset + element), memoryStruct.bytes[result_counter]); 235 | results.add(memoryStruct.bytes[result_counter]); 236 | result_counter++; 237 | } 238 | 239 | } 240 | queue_offset += left_num; 241 | } 242 | return results; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/utils/MessageB64Serialization.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.utils; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public class MessageB64Serialization { 6 | final static int FIXED_PART_LEN = 10; // assume FIXED_PART_LEN >=2, otherwise program is not correct 7 | final static int BASE64_INFO_LEN = 2; 8 | final static int INDEX_LEN = 4; 9 | final static int VARYING_VERIFY_LEN = 4; 10 | final static int COPY_PART_LEN = FIXED_PART_LEN - INDEX_LEN; 11 | 12 | final static byte[] magicArr = {'B', 'L', 'I', 'N', 'K'}; 13 | final static int MAX_FIVE_BITS_INT = 0x1f; // 31, for varyingLen Serialization 14 | 15 | // mutable: message is not usable later 16 | public static byte[] SerializeBase64Decoding(byte[] message, int len) { 17 | int serialize_len = len - FIXED_PART_LEN; 18 | byte padding_chars = (byte) ((4 - serialize_len % 4) % 4); 19 | // 1st: fixed part 20 | int serialization_base64_output_length = 3 * (serialize_len / 4 + (serialize_len % 4 == 0 ? 0 : 1)); 21 | byte[] serialized = new byte[serialization_base64_output_length + FIXED_PART_LEN + 1]; 22 | 23 | System.arraycopy(message, serialize_len, serialized, serialization_base64_output_length, FIXED_PART_LEN); 24 | // 2nd: padding for mutable message 25 | System.arraycopy(magicArr, 0, message, serialize_len, padding_chars); 26 | // 3rd: lookup with chromium base64 decoding 27 | ChromiumBase64.ChromiumBase64Decode(serialized, message, serialize_len + padding_chars); 28 | // 4th: record padding chars 29 | serialized[serialization_base64_output_length + FIXED_PART_LEN] = padding_chars; // indicate number of padding chars 30 | return serialized; 31 | } 32 | 33 | public static byte[] DeserializeBase64Encoding(byte[] serialized, int start_off, int total_serialized_len) { 34 | int serialize_len = total_serialized_len - FIXED_PART_LEN - 1; 35 | 36 | // 1st: must be the accurate size to make it correct later without extra copying 37 | byte[] deserialized = new byte[serialize_len / 3 * 4 + FIXED_PART_LEN - serialized[start_off + total_serialized_len - 1]]; 38 | // 2nd: deserialization 39 | int length = ChromiumBase64.ChromiumBase64Encode(deserialized, serialized, start_off, serialize_len); 40 | // 3rd: copy the fixed part 41 | System.arraycopy(serialized, serialize_len + start_off, deserialized, length - serialized[start_off + 42 | total_serialized_len - 1], FIXED_PART_LEN); 43 | return deserialized; 44 | } 45 | 46 | public static void SerializeBase64DecodingSkipIndexOff(byte[] message, int len, byte[] serialized, int offset) { 47 | int serialize_len = len - FIXED_PART_LEN; 48 | // should be optimized to be always 0 49 | byte padding_chars = (byte) ((4 - serialize_len % 4) % 4); 50 | // 1st: fixed part 51 | int serialization_base64_output_length = 3 * (serialize_len / 4 + (serialize_len % 4 == 0 ? 0 : 1)); 52 | 53 | System.arraycopy(message, serialize_len, serialized, offset + serialization_base64_output_length, BASE64_INFO_LEN); 54 | System.arraycopy(message, serialize_len + BASE64_INFO_LEN + INDEX_LEN, serialized, 55 | offset + serialization_base64_output_length + BASE64_INFO_LEN, VARYING_VERIFY_LEN); 56 | 57 | 58 | // 2nd: padding for mutable message 59 | System.arraycopy(magicArr, 0, message, serialize_len, padding_chars); 60 | // 3rd: lookup with chromium base64 decoding 61 | ChromiumBase64.ChromiumBase64DecodeOff(serialized, offset, message, serialize_len + padding_chars); 62 | // 4th: record padding chars 63 | serialized[serialization_base64_output_length + COPY_PART_LEN] = padding_chars; // indicate number of padding chars 64 | } 65 | 66 | public static byte[] SerializeBase64DecodingSkipIndex(byte[] message, int len) { 67 | int serialize_len = len - FIXED_PART_LEN; 68 | // should be optimized to be always 0 69 | byte padding_chars = (byte) ((4 - serialize_len % 4) % 4); 70 | // 1st: fixed part 71 | int serialization_base64_output_length = 3 * (serialize_len / 4 + (serialize_len % 4 == 0 ? 0 : 1)); 72 | byte[] serialized = new byte[serialization_base64_output_length + COPY_PART_LEN + 1]; 73 | 74 | System.arraycopy(message, serialize_len, serialized, serialization_base64_output_length, BASE64_INFO_LEN); 75 | System.arraycopy(message, serialize_len + BASE64_INFO_LEN + INDEX_LEN, serialized, 76 | serialization_base64_output_length + BASE64_INFO_LEN, VARYING_VERIFY_LEN); 77 | 78 | 79 | // 2nd: padding for mutable message 80 | System.arraycopy(magicArr, 0, message, serialize_len, padding_chars); 81 | // 3rd: lookup with chromium base64 decoding 82 | ChromiumBase64.ChromiumBase64Decode(serialized, message, serialize_len + padding_chars); 83 | // 4th: record padding chars 84 | serialized[serialization_base64_output_length + COPY_PART_LEN] = padding_chars; // indicate number of padding chars 85 | return serialized; 86 | } 87 | 88 | public static byte[] DeserializeBase64EncodingAddIndex(byte[] serialized, int start_off, int total_serialized_len, int index) { 89 | int serialize_len = total_serialized_len - COPY_PART_LEN - 1; 90 | 91 | // 1st: must be the accurate size to make it correct later without extra copying 92 | byte[] deserialized = new byte[serialize_len / 3 * 4 + FIXED_PART_LEN - serialized[start_off + total_serialized_len - 1]]; 93 | // 2nd: deserialization 94 | int length = ChromiumBase64.ChromiumBase64Encode(deserialized, serialized, start_off, serialize_len); 95 | // 3rd: copy the fixed part 96 | int new_off = length - serialized[start_off + total_serialized_len - 1]; 97 | // 1) remaining base64 98 | System.arraycopy(serialized, serialize_len + start_off, deserialized, new_off, BASE64_INFO_LEN); 99 | new_off += BASE64_INFO_LEN; 100 | // 2) index: 101 | deserialized[new_off] = (byte) (index & 0xff); 102 | deserialized[new_off + 1] = (byte) ((index >> 8) & 0xff); 103 | deserialized[new_off + 2] = (byte) ((index >> 16) & 0xff); 104 | new_off += INDEX_LEN; 105 | // 3) other verification parts 106 | System.arraycopy(serialized, serialize_len + start_off + BASE64_INFO_LEN, deserialized, new_off, VARYING_VERIFY_LEN); 107 | 108 | return deserialized; 109 | } 110 | 111 | 112 | // only for normal size message (58 raw bytes) 113 | public static void DeserializeBase64EncodingAddIndexNormalSize(byte[] serialized, int start_off, int total_serialized_len, int index, byte[] deserialized) { 114 | int serialize_len = total_serialized_len - COPY_PART_LEN - 1; 115 | 116 | // 1st: must be the accurate size to make it correct later without extra copying 117 | // 2nd: deserialization 118 | int length = ChromiumBase64.ChromiumBase64Encode(deserialized, serialized, start_off, serialize_len); 119 | // 3rd: copy the fixed part 120 | int new_off = length - serialized[start_off + total_serialized_len - 1]; 121 | // 1) remaining base64 122 | System.arraycopy(serialized, serialize_len + start_off, deserialized, new_off, BASE64_INFO_LEN); 123 | new_off += BASE64_INFO_LEN; 124 | // 2) index: 125 | deserialized[new_off] = (byte) (index & 0xff); 126 | deserialized[new_off + 1] = (byte) ((index >> 8) & 0xff); 127 | deserialized[new_off + 2] = (byte) ((index >> 16) & 0xff); 128 | new_off += INDEX_LEN; 129 | // 3) other verification parts 130 | System.arraycopy(serialized, serialize_len + start_off + BASE64_INFO_LEN, deserialized, new_off, VARYING_VERIFY_LEN); 131 | } 132 | 133 | // mutable: message is not usable later 134 | static int SerializeBase64DecodingByteBuffer(byte[] message, int len, ByteBuffer serialized) { 135 | int serialize_len = len - FIXED_PART_LEN; 136 | byte padding_chars = (byte) ((4 - serialize_len % 4) % 4); 137 | 138 | // 1st: fixed part 139 | int serialization_base64_output_length = 3 * (serialize_len / 4 + (serialize_len % 4 == 0 ? 0 : 1)); 140 | for (int i = 0; i < FIXED_PART_LEN; i++) { 141 | serialized.put(serialization_base64_output_length + i, message[serialize_len + i]); 142 | } 143 | // 2nd: padding for mutable message 144 | System.arraycopy(magicArr, 0, message, serialize_len, padding_chars); 145 | // 3rd: lookup with chromium base64 decoding 146 | ChromiumBase64.ChromiumBase64DecodeByteBuffer(serialized, message, serialize_len + padding_chars); 147 | // 4th: record padding chars 148 | serialized.position(serialization_base64_output_length + FIXED_PART_LEN); 149 | serialized.put(padding_chars); // indicate number of padding chars 150 | 151 | serialized.flip(); 152 | return serialization_base64_output_length + FIXED_PART_LEN + 1; 153 | } 154 | 155 | public static byte[] DeserializeBase64EncodingByteBuffer(ByteBuffer serialized, int total_serialized_len) { 156 | int serialize_len = total_serialized_len - FIXED_PART_LEN - 1; 157 | 158 | // 1st: must be the accurate size to make it correct later without extra copying 159 | byte padding_chars = serialized.get(total_serialized_len - 1); 160 | byte[] deserialized = new byte[serialize_len / 3 * 4 + FIXED_PART_LEN - padding_chars]; 161 | // 2nd: deserialization 162 | int length = ChromiumBase64.ChromiumBase64EncodeByteBuffer(deserialized, serialized, serialize_len); 163 | // 3rd: copy the fixed part 164 | for (int i = 0; i < FIXED_PART_LEN; i++) { 165 | deserialized[i + length - padding_chars] = serialized.get(serialize_len + i); 166 | } 167 | return deserialized; 168 | } 169 | 170 | // attention: 1) assume serialized allocated the same size as message, worst case same length 171 | // 2) 2 extra bytes is for length of the raw message 172 | public static ByteBuffer SerializeVaryingLen(byte[] message, int messageLen) { 173 | // add the header to indicate raw message varying-length part size, similar to padding chars 174 | ByteBuffer serializedBuffer = ByteBuffer.allocate(messageLen + 2); // assume clear at the allocation 175 | byte[] serialized = serializedBuffer.array(); 176 | 177 | int serialize_len = messageLen - FIXED_PART_LEN; 178 | int headerLen; 179 | if (messageLen < 128) { 180 | serialized[0] = (byte) (messageLen - FIXED_PART_LEN); 181 | headerLen = 1; 182 | } else { 183 | int tmp = (messageLen - FIXED_PART_LEN); // tmp high bits are zeros 184 | serialized[0] = (byte) ((tmp >>> 7) | 0x80); // assume < 32767 185 | serialized[1] = (byte) (tmp & 0x7f); // low 7 bits 186 | headerLen = 2; 187 | } 188 | int next_extra_3bits_idx = 5 * serialize_len; 189 | int next_5bits_idx = 0; 190 | 191 | // attention: message is not usable later 192 | for (int i = 0; i < serialize_len; i++) { 193 | message[i] = (byte) (message[i] >= 'a' ? message[i] - 'a' : message[i] - '0' + 26); 194 | } 195 | 196 | // 1) construct the compressed part 197 | for (int i = 0; i < serialize_len; i++) { 198 | int cur_uchar = message[i] & 0xff; 199 | int expand_uchar = cur_uchar < MAX_FIVE_BITS_INT ? (cur_uchar << 11) : (MAX_FIVE_BITS_INT << 11); 200 | 201 | int shift_bits = (next_5bits_idx & 0x7); 202 | expand_uchar >>= shift_bits; 203 | int idx = (next_5bits_idx >> 3) + headerLen; 204 | serialized[idx] |= (expand_uchar >> 8); 205 | serialized[idx + 1] |= (expand_uchar & 0xff); 206 | next_5bits_idx += 5; 207 | 208 | if (cur_uchar >= MAX_FIVE_BITS_INT) { 209 | // do extra bits operations 210 | expand_uchar = ((cur_uchar - MAX_FIVE_BITS_INT) << 13); 211 | shift_bits = (next_extra_3bits_idx & 0x7); 212 | expand_uchar >>= shift_bits; 213 | // assume little-endian 214 | idx = (next_extra_3bits_idx >> 3) + headerLen; 215 | serialized[idx] |= (expand_uchar >> 8); 216 | serialized[idx + 1] |= (expand_uchar & 0xff); 217 | next_extra_3bits_idx += 3; 218 | } 219 | } 220 | 221 | // 2) left FIXED_PART_LEN, should use memcpy 222 | int start_copy_byte_idx = headerLen + (next_extra_3bits_idx >> 3) + ((next_extra_3bits_idx & 0x7) != 0 ? 1 : 0); 223 | 224 | System.arraycopy(message, serialize_len, serialized, start_copy_byte_idx, FIXED_PART_LEN); 225 | serializedBuffer.position(start_copy_byte_idx + FIXED_PART_LEN); 226 | return serializedBuffer; 227 | } 228 | 229 | public static byte[] DeserializeVaryingLen(ByteBuffer serializedBuffer) { 230 | // get the length of varying part 231 | byte[] serialized = serializedBuffer.array(); 232 | int varying_byte_len; 233 | int headerLen; 234 | if ((serialized[0] & 0x80) == 0) { 235 | varying_byte_len = serialized[0]; 236 | headerLen = 1; 237 | } else { 238 | varying_byte_len = (((serialized[0] & 0x7f) << 0x7) + serialized[1]); 239 | headerLen = 2; 240 | } 241 | int next_extra_3bits_idx = 5 * varying_byte_len; 242 | int next_5bits_idx = 0; 243 | 244 | byte[] deserialized = new byte[varying_byte_len + FIXED_PART_LEN]; 245 | // deserialize 246 | for (int i = 0; i < varying_byte_len; i++) { 247 | int idx = (next_5bits_idx >> 3) + headerLen; 248 | int value = ((serialized[idx] & 0xff) << 8) + (serialized[idx + 1] & 0xff); 249 | value = (value >> (11 - (next_5bits_idx & 0x7))) & MAX_FIVE_BITS_INT; 250 | if (value != MAX_FIVE_BITS_INT) { 251 | deserialized[i] = (byte) (value < 26 ? 'a' + value : value - 26 + '0'); 252 | } else { 253 | idx = (next_extra_3bits_idx >> 3) + headerLen; 254 | value = ((serialized[idx] & 0xff) << 8) + (serialized[idx + 1] & 0xff); 255 | value = (value >> (13 - (next_extra_3bits_idx & 0x7))) & 0x7; 256 | deserialized[i] = (byte) (value + '5'); 257 | next_extra_3bits_idx += 3; 258 | } 259 | next_5bits_idx += 5; 260 | } 261 | 262 | // 2) copy the fixed part 263 | System.arraycopy(serialized, headerLen + 264 | (next_extra_3bits_idx >> 3) + ((next_extra_3bits_idx & 0x7) != 0 ? 1 : 0), 265 | deserialized, varying_byte_len, FIXED_PART_LEN); 266 | return deserialized; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/DemoTester.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging; 2 | 3 | import java.util.Collection; 4 | import java.util.Random; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.ConcurrentMap; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | import java.util.concurrent.atomic.AtomicLong; 9 | 10 | //这是评测程序的一个demo版本,其评测逻辑与实际评测程序基本类似,但是比实际评测简单很多 11 | //该评测程序主要便于选手在本地优化和调试自己的程序 12 | 13 | public class DemoTester { 14 | private static final byte[] messageTemplate = new byte[]{ 15 | 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 16 | 'C', 'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E', 17 | 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', 18 | 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 19 | 'K', 'K', 'K', 'K', 'L', 'L', 'L', 'L', 'M', 'M', 20 | 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', 21 | 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 22 | 'R', 'R', 'S', 'S', 'S', 'S', 'T', 'T', 'T', 'T', 23 | 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', 24 | 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 25 | 'Z', 'Z', 'Z', 'Z', 'a', 'a', 'a', 'a', 'b', 'b', 26 | 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', 27 | 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 28 | 'g', 'g', 'h', 'h', 'h', 'h', 'i', 'i', 'i', 'i', 29 | 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', 30 | 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 31 | 'o', 'o', 'o', 'o', 'p', 'p', 'p', 'p', 'q', 'q', 32 | 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', 33 | 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 34 | 'v', 'v', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x', 35 | 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', 36 | '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', 37 | '3', '3', '3', '3', '4', '4', '4', '4', '5', '5', 38 | '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', 39 | '8', '8', '8', '8', '9', '9', '9', '9', 40 | }; 41 | 42 | private static final byte[] bigMessageTemplate; 43 | 44 | static { 45 | bigMessageTemplate = new byte[32768]; 46 | for (int i = 0; i < bigMessageTemplate.length; i++) { 47 | bigMessageTemplate[i] = messageTemplate[i % messageTemplate.length]; 48 | } 49 | } 50 | 51 | // used by producer, base64 string followed by fixed length bytes 52 | private static byte[] produce(int index, int base64Len) { 53 | byte[] bytes = new byte[base64Len + 8]; 54 | System.arraycopy(bigMessageTemplate, 0, bytes, 0, base64Len); 55 | // construct index for indexing checking 56 | for (int i = 0; i < 4; i++) { 57 | bytes[(base64Len + i)] = ((byte) (index >>> 8 * i)); 58 | } 59 | // hash code for correctness checking 60 | System.arraycopy(bigMessageTemplate, 0, bytes, base64Len + 4, 4); 61 | return bytes; 62 | } 63 | 64 | // used by consumer, index and integrity checking 65 | private static boolean verify(byte[] message, int index) { 66 | // 1st: for index checking 67 | int verifyingIdx = 0; 68 | for (int i = 0; i < 4; i++) { 69 | byte b = message[(message.length - 8 + i)]; 70 | verifyingIdx += ((b & 0xFF) << 8 * i); 71 | } 72 | if (verifyingIdx != index) { 73 | System.out.println(verifyingIdx + " , " + index); 74 | System.out.println("index checking fail"); 75 | return false; 76 | } 77 | // 2nd: for integrity checking 78 | int integrityStart = message.length - 4; 79 | for (int i = 0; i < 4; i++) { 80 | if (message[i] != message[integrityStart + i]) { 81 | System.out.println(message.length); 82 | System.out.println("idx:" + verifyingIdx); 83 | System.out.println(message[i] + message[integrityStart + i]); 84 | System.out.println("integrity checking fail"); 85 | return false; 86 | } 87 | } 88 | return true; 89 | } 90 | 91 | public static void main(String args[]) throws Exception { 92 | //评测相关配置 93 | //发送阶段的发送数量,也即发送阶段必须要在规定时间内把这些消息发送完毕方可 94 | int msgNum = 10000000; 95 | //发送阶段的最大持续时间,也即在该时间内,如果消息依然没有发送完毕,则退出评测 96 | int sendTime = 2000 * 1000; 97 | //消费阶段的最大持续时间,也即在该时间内,如果消息依然没有消费完毕,则退出评测 98 | int checkTime = 1600 * 1000; 99 | //队列的数量 100 | int queueNum = 1000000; // attention: currently need to be exact 1000000 101 | //正确性检测的次数 102 | int checkNum = queueNum; 103 | //消费阶段的总队列数量 104 | int checkQueueNum = queueNum / 10; 105 | //发送的线程数量 106 | int sendTsNum = 10; 107 | //消费的线程数量 108 | int checkTsNum = 10; // attention: currently should be exact 10, for the synchronization purpose 109 | 110 | ConcurrentMap queueNumMap = new ConcurrentHashMap<>(); 111 | for (int i = 0; i < queueNum; i++) { 112 | queueNumMap.put("Queue-" + i, new AtomicInteger(0)); 113 | } 114 | 115 | QueueStore queueStore = null; 116 | 117 | try { 118 | Class queueStoreClass = Class.forName("io.openmessaging.DefaultQueueStoreImpl"); 119 | queueStore = (QueueStore) queueStoreClass.newInstance(); 120 | } catch (Throwable t) { 121 | t.printStackTrace(); 122 | System.exit(-1); 123 | } 124 | 125 | //Step1: 发送消息 126 | long sendStart = System.currentTimeMillis(); 127 | long maxTimeStamp = System.currentTimeMillis() + sendTime; 128 | AtomicLong sendCounter = new AtomicLong(0); 129 | Thread[] sends = new Thread[sendTsNum]; 130 | for (int i = 0; i < sendTsNum; i++) { 131 | sends[i] = new Thread(new Producer(queueStore, i, maxTimeStamp, msgNum, sendCounter, queueNumMap)); 132 | } 133 | for (int i = 0; i < sendTsNum; i++) { 134 | sends[i].start(); 135 | } 136 | for (int i = 0; i < sendTsNum; i++) { 137 | sends[i].join(); 138 | } 139 | long sendSend = System.currentTimeMillis(); 140 | System.out.printf("Send: %d ms Num:%d\n", sendSend - sendStart, sendCounter.get()); 141 | long maxCheckTime = System.currentTimeMillis() + checkTime; 142 | 143 | //Step2: 索引的正确性校验 144 | long indexCheckStart = System.currentTimeMillis(); 145 | AtomicLong indexCheckCounter = new AtomicLong(0); 146 | Thread[] indexChecks = new Thread[checkTsNum]; 147 | for (int i = 0; i < sendTsNum; i++) { 148 | indexChecks[i] = new Thread(new IndexChecker(queueStore, i, maxCheckTime, checkNum, indexCheckCounter, queueNumMap)); 149 | } 150 | for (int i = 0; i < sendTsNum; i++) { 151 | indexChecks[i].start(); 152 | } 153 | for (int i = 0; i < sendTsNum; i++) { 154 | indexChecks[i].join(); 155 | } 156 | long indexCheckEnd = System.currentTimeMillis(); 157 | System.out.printf("Index Check: %d ms Num:%d\n", indexCheckEnd - indexCheckStart, indexCheckCounter.get()); 158 | 159 | //Step3: 消费消息,并验证顺序性 160 | long checkStart = System.currentTimeMillis(); 161 | Random random = new Random(); 162 | AtomicLong checkCounter = new AtomicLong(0); 163 | Thread[] checks = new Thread[checkTsNum]; 164 | for (int i = 0; i < sendTsNum; i++) { 165 | int eachCheckQueueNum = checkQueueNum / checkTsNum; 166 | ConcurrentMap offsets = new ConcurrentHashMap<>(); 167 | for (int j = 0; j < eachCheckQueueNum; j++) { 168 | String queueName = "Queue-" + random.nextInt(queueNum); 169 | while (offsets.containsKey(queueName)) { 170 | queueName = "Queue-" + random.nextInt(queueNum); 171 | } 172 | offsets.put(queueName, queueNumMap.get(queueName)); 173 | } 174 | checks[i] = new Thread(new Consumer(queueStore, i, maxCheckTime, checkCounter, offsets)); 175 | } 176 | for (int i = 0; i < sendTsNum; i++) { 177 | checks[i].start(); 178 | } 179 | for (int i = 0; i < sendTsNum; i++) { 180 | checks[i].join(); 181 | } 182 | long checkEnd = System.currentTimeMillis(); 183 | System.out.printf("Check: %d ms Num: %d\n", checkEnd - checkStart, checkCounter.get()); 184 | 185 | //评测结果 186 | System.out.printf("Tps:%f\n", ((sendCounter.get() + checkCounter.get() + indexCheckCounter.get()) + 0.1) * 1000 / ((sendSend - sendStart) + (checkEnd - checkStart) + (indexCheckEnd - indexCheckStart))); 187 | } 188 | 189 | static class Producer implements Runnable { 190 | 191 | private AtomicLong counter; 192 | private ConcurrentMap queueCounter; 193 | private long maxMsgNum; 194 | private QueueStore queueStore; 195 | private int number; 196 | private long maxTimeStamp; 197 | 198 | public Producer(QueueStore queueStore, int number, long maxTimeStamp, int maxMsgNum, AtomicLong counter, ConcurrentMap queueCounter) { 199 | this.counter = counter; 200 | this.maxMsgNum = maxMsgNum; 201 | this.queueCounter = queueCounter; 202 | this.number = number; 203 | this.queueStore = queueStore; 204 | this.maxTimeStamp = maxTimeStamp; 205 | } 206 | 207 | @Override 208 | public void run() { 209 | long count; 210 | while ((count = counter.getAndIncrement()) < maxMsgNum && System.currentTimeMillis() <= maxTimeStamp) { 211 | try { 212 | String queueName = "Queue-" + count % queueCounter.size(); 213 | synchronized (queueCounter.get(queueName)) { 214 | int inQueueOff = queueCounter.get(queueName).getAndIncrement(); 215 | // currently do not support < 50 ... 216 | queueStore.put(queueName, produce(inQueueOff, inQueueOff % 100 == 1 ? 1024 : 50)); 217 | // queueStore.put(queueName, produce(inQueueOff, 50)); 218 | } 219 | } catch (Throwable t) { 220 | t.printStackTrace(); 221 | System.exit(-1); 222 | } 223 | } 224 | } 225 | } 226 | 227 | static class IndexChecker implements Runnable { 228 | 229 | private AtomicLong counter; 230 | private long maxMsgNum; 231 | private QueueStore queueStore; 232 | private long maxTimeStamp; 233 | private int number; 234 | private ConcurrentMap queueCounter; 235 | 236 | public IndexChecker(QueueStore queueStore, int number, long maxTimeStamp, int maxMsgNum, AtomicLong counter, ConcurrentMap queueCounter) { 237 | this.counter = counter; 238 | this.maxMsgNum = maxMsgNum; 239 | this.queueStore = queueStore; 240 | this.number = number; 241 | this.queueCounter = queueCounter; 242 | this.maxTimeStamp = maxTimeStamp; 243 | } 244 | 245 | @Override 246 | public void run() { 247 | Random random = new Random(); 248 | while (counter.getAndIncrement() < maxMsgNum && System.currentTimeMillis() <= maxTimeStamp) { 249 | try { 250 | String queueName = "Queue-" + random.nextInt(queueCounter.size()); 251 | int index = random.nextInt(queueCounter.get(queueName).get()) - 10; 252 | if (index < 0) index = 0; 253 | Collection msgs = queueStore.get(queueName, index, 10); 254 | 255 | int cnt = 0; 256 | for (byte[] msg : msgs) { 257 | if (!verify(msg, index + cnt)) { 258 | System.out.println("Check error"); 259 | System.out.println(queueName); 260 | System.exit(-1); 261 | } 262 | cnt++; 263 | } 264 | } catch (Throwable t) { 265 | t.printStackTrace(); 266 | System.exit(-1); 267 | 268 | } 269 | } 270 | } 271 | } 272 | 273 | static class Consumer implements Runnable { 274 | 275 | private AtomicLong counter; 276 | private QueueStore queueStore; 277 | private ConcurrentMap offsets; 278 | private long maxTimeStamp; 279 | private int number; 280 | 281 | public Consumer(QueueStore queueStore, int number, long maxTimeStamp, AtomicLong counter, ConcurrentMap offsets) { 282 | this.counter = counter; 283 | this.queueStore = queueStore; 284 | this.offsets = offsets; 285 | this.maxTimeStamp = maxTimeStamp; 286 | this.number = number; 287 | } 288 | 289 | @Override 290 | public void run() { 291 | ConcurrentMap pullOffsets = new ConcurrentHashMap<>(); 292 | for (String queueName : offsets.keySet()) { 293 | pullOffsets.put(queueName, new AtomicInteger(0)); 294 | } 295 | while (pullOffsets.size() > 0 && System.currentTimeMillis() <= maxTimeStamp) { 296 | try { 297 | for (String queueName : pullOffsets.keySet()) { 298 | int index = pullOffsets.get(queueName).get(); 299 | Collection msgs = queueStore.get(queueName, index, 10); 300 | if (msgs != null && msgs.size() > 0) { 301 | pullOffsets.get(queueName).getAndAdd(msgs.size()); 302 | int cnt = 0; 303 | for (byte[] msg : msgs) { 304 | if (!verify(msg, index + cnt)) { 305 | System.out.println("Check error"); 306 | System.exit(-1); 307 | } 308 | cnt++; 309 | } 310 | 311 | counter.addAndGet(msgs.size()); 312 | } 313 | if (msgs == null || msgs.size() < 10) { 314 | if (pullOffsets.get(queueName).get() != offsets.get(queueName).get()) { 315 | System.out.printf("Queue Number Error"); 316 | System.exit(-1); 317 | } 318 | pullOffsets.remove(queueName); 319 | } 320 | } 321 | } catch (Throwable t) { 322 | t.printStackTrace(); 323 | System.exit(-1); 324 | } 325 | } 326 | } 327 | } 328 | } 329 | 330 | 331 | -------------------------------------------------------------------------------- /src/main/java/io/openmessaging/utils/ChromiumBase64.java: -------------------------------------------------------------------------------- 1 | package io.openmessaging.utils; 2 | 3 | 4 | import java.nio.ByteBuffer; 5 | import java.nio.ByteOrder; 6 | 7 | public class ChromiumBase64 { 8 | final private static byte[] e0 = { 9 | 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 10 | 'C', 'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E', 11 | 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', 12 | 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 13 | 'K', 'K', 'K', 'K', 'L', 'L', 'L', 'L', 'M', 'M', 14 | 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', 15 | 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 16 | 'R', 'R', 'S', 'S', 'S', 'S', 'T', 'T', 'T', 'T', 17 | 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', 18 | 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 19 | 'Z', 'Z', 'Z', 'Z', 'a', 'a', 'a', 'a', 'b', 'b', 20 | 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', 21 | 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 22 | 'g', 'g', 'h', 'h', 'h', 'h', 'i', 'i', 'i', 'i', 23 | 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', 24 | 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 25 | 'o', 'o', 'o', 'o', 'p', 'p', 'p', 'p', 'q', 'q', 26 | 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', 27 | 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 28 | 'v', 'v', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x', 29 | 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', 30 | '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', 31 | '3', '3', '3', '3', '4', '4', '4', '4', '5', '5', 32 | '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', 33 | '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', 34 | '+', '+', '/', '/', '/', '/' 35 | }; 36 | 37 | final private static byte[] e1 = { 38 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 39 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 40 | 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 41 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 42 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 43 | 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 44 | '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 45 | 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 46 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 47 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 48 | 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 49 | 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', 50 | '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 51 | 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 52 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 53 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 54 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 55 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 56 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 57 | '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 58 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 59 | 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 60 | 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 61 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 62 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', 63 | '6', '7', '8', '9', '+', '/' 64 | }; 65 | 66 | final private static byte e2[] = { 67 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 68 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 69 | 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 70 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 71 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 72 | 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 73 | '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 74 | 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 75 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 76 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 77 | 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 78 | 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', 79 | '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 80 | 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 81 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 82 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 83 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 84 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 85 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 86 | '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 87 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 88 | 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 89 | 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 90 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 91 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', 92 | '6', '7', '8', '9', '+', '/' 93 | }; 94 | 95 | /* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */ 96 | final private static int[] d0 = { 97 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 98 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 99 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 100 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 101 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 102 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 103 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 104 | 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, 105 | 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, 106 | 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, 107 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 108 | 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, 109 | 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, 110 | 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, 111 | 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, 112 | 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 113 | 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, 114 | 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, 115 | 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, 116 | 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, 117 | 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, 118 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 119 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 120 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 121 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 122 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 123 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 124 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 125 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 126 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 127 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 128 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 129 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 130 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 131 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 132 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 133 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 134 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 135 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 136 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 137 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 138 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 139 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 140 | }; 141 | 142 | final private static int[] d1 = { 143 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 144 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 145 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 146 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 147 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 148 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 149 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 150 | 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, 151 | 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, 152 | 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, 153 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 154 | 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, 155 | 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, 156 | 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, 157 | 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, 158 | 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 159 | 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, 160 | 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, 161 | 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, 162 | 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, 163 | 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 164 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 165 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 166 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 167 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 168 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 169 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 170 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 171 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 172 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 173 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 174 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 175 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 176 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 177 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 178 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 179 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 180 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 181 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 182 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 183 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 184 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 185 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 186 | }; 187 | 188 | 189 | final private static int[] d2 = { 190 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 191 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 192 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 193 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 194 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 195 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 196 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 197 | 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, 198 | 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, 199 | 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, 200 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 201 | 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, 202 | 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, 203 | 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, 204 | 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, 205 | 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 206 | 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, 207 | 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, 208 | 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, 209 | 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, 210 | 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 211 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 212 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 213 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 214 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 215 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 216 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 217 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 218 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 219 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 220 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 221 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 222 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 223 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 224 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 225 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 226 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 227 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 228 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 229 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 230 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 231 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 232 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 233 | }; 234 | 235 | 236 | final private static int[] d3 = { 237 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 238 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 239 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 240 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 241 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 242 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 243 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 244 | 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, 245 | 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, 246 | 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, 247 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 248 | 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, 249 | 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, 250 | 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, 251 | 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, 252 | 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 253 | 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, 254 | 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, 255 | 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, 256 | 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, 257 | 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 258 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 259 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 260 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 261 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 262 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 263 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 264 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 265 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 266 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 267 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 268 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 269 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 270 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 271 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 272 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 273 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 274 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 275 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 276 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 277 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 278 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 279 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 280 | }; 281 | 282 | static int ChromiumBase64Encode(byte[] dest, byte[] str, int start_off, int len) { 283 | int size_p = 0; 284 | /* unsigned here is important! */ // for correct offset loopup 285 | byte t1, t2, t3; 286 | for (int i = 0; i < len - 2; i += 3) { 287 | t1 = str[start_off + i]; 288 | t2 = str[start_off + i + 1]; 289 | t3 = str[start_off + i + 2]; 290 | dest[size_p] = e0[t1 & 0xff]; 291 | dest[size_p + 1] = e1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) & 0xff]; 292 | dest[size_p + 2] = e1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) & 0xff]; 293 | dest[size_p + 3] = e2[t3 & 0xff]; 294 | size_p += 4; 295 | } 296 | return size_p; 297 | } 298 | 299 | static void ChromiumBase64Decode(byte[] dest, byte[] src, int len) { 300 | int chunks = len / 4; 301 | int size_p = 0; 302 | int offset_y = 0; 303 | for (int i = 0; i < chunks; ++i, offset_y += 4) { 304 | int x = d0[src[offset_y]] | d1[src[1 + offset_y]] | d2[src[2 + offset_y]] | d3[src[3 + offset_y]]; 305 | dest[size_p++] = (byte) ((x) & 0xff); //x[0] 306 | dest[size_p++] = (byte) ((x >>> 8) & 0xff); //x[1] 307 | dest[size_p++] = (byte) ((x >>> 16) & 0xff); //x[2] 308 | } 309 | } 310 | 311 | static void ChromiumBase64DecodeOff(byte[] dest, int off, byte[] src, int len) { 312 | int chunks = len / 4; 313 | int size_p = off; 314 | int offset_y = 0; 315 | for (int i = 0; i < chunks; ++i, offset_y += 4) { 316 | int x = d0[src[offset_y]] | d1[src[1 + offset_y]] | d2[src[2 + offset_y]] | d3[src[3 + offset_y]]; 317 | dest[size_p++] = (byte) ((x) & 0xff); //x[0] 318 | dest[size_p++] = (byte) ((x >>> 8) & 0xff); //x[1] 319 | dest[size_p++] = (byte) ((x >>> 16) & 0xff); //x[2] 320 | } 321 | } 322 | 323 | // assume reusable bytebuffer and already flipped 324 | static int ChromiumBase64EncodeByteBuffer(byte[] dest, ByteBuffer srcByteBuffer, int len) { 325 | int size_p = 0; 326 | /* unsigned here is important! */ // for correct offset loopup 327 | byte t1, t2, t3; 328 | for (int i = 0; i < len - 2; i += 3) { 329 | t1 = srcByteBuffer.get(i); 330 | t2 = srcByteBuffer.get(i + 1); 331 | t3 = srcByteBuffer.get(i + 2); 332 | dest[size_p] = e0[t1 & 0xff]; 333 | dest[size_p + 1] = e1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) & 0xff]; 334 | dest[size_p + 2] = e1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) & 0xff]; 335 | dest[size_p + 3] = e2[t3 & 0xff]; 336 | size_p += 4; 337 | } 338 | return size_p; 339 | } 340 | 341 | // assume reuse DirectByteBuffer reusable 342 | static void ChromiumBase64DecodeByteBuffer(ByteBuffer dest, byte[] src, int len) { 343 | dest.clear(); // for writing 344 | 345 | int chunks = len / 4; 346 | int offset_y = 0; 347 | for (int i = 0; i < chunks; ++i, offset_y += 4) { 348 | int x = d0[src[offset_y]] | d1[src[1 + offset_y]] | d2[src[2 + offset_y]] | d3[src[3 + offset_y]]; 349 | dest.put((byte) ((x >>> 0) & 0xff)); 350 | dest.put((byte) ((x >>> 8) & 0xff)); 351 | dest.put((byte) ((x >>> 16) & 0xff)); 352 | } 353 | } 354 | 355 | private static void TestEndian() { 356 | int x = 1; 357 | if (ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)) { 358 | System.out.println("Big-endian"); 359 | } else { 360 | System.out.println("Little-endian"); 361 | } 362 | for (int i = 0; i < 4; i++) { 363 | System.out.println("mem addr:[" + i + "]: " + (x >>> ((3 - i) * 8) & 0xff)); 364 | } 365 | } 366 | 367 | private static void TestRawArr(byte[] chars) { 368 | // 1st: serialization 369 | byte[] serialized = new byte[chars.length / 4 * 3]; 370 | ChromiumBase64Decode(serialized, chars, chars.length); 371 | 372 | System.out.println("serialized: " + serialized.length); 373 | for (byte aChar : serialized) { 374 | System.out.print(Integer.toString(aChar & 0xff, 16)); 375 | System.out.print(","); 376 | } 377 | System.out.println(); 378 | byte[] tmp = new byte[]{0x6a, (byte) 0xf7, 0x1d, 0x7b, 0x4d, 0x40, 0x73, 0x6d, (byte) 0xf8}; 379 | System.out.println("ground truth of serialized: " + tmp.length); 380 | for (byte ch : tmp) { 381 | System.out.print(Integer.toString(ch & 0xff, 16) + ","); 382 | } 383 | System.out.println(); 384 | 385 | // 2nd: deserialization 386 | byte[] deserialized = new byte[chars.length]; 387 | ChromiumBase64Encode(deserialized, serialized, 0, serialized.length); 388 | System.out.println(new String(chars)); 389 | System.out.println(new String(deserialized)); 390 | } 391 | 392 | private static void TestByteBuffer(byte[] chars) { 393 | // 1st: serialization 394 | ByteBuffer serialized = ByteBuffer.allocateDirect(1024); 395 | ChromiumBase64DecodeByteBuffer(serialized, chars, chars.length); 396 | 397 | System.out.println(); 398 | byte[] tmp = new byte[]{0x6a, (byte) 0xf7, 0x1d, 0x7b, 0x4d, 0x40, 0x73, 0x6d, (byte) 0xf8}; 399 | System.out.println("ground truth of serialized: " + tmp.length); 400 | for (byte ch : tmp) { 401 | System.out.print(Integer.toString(ch & 0xff, 16) + ","); 402 | } 403 | System.out.println(); 404 | 405 | // 2nd: deserialization 406 | byte[] deserialized = new byte[chars.length]; 407 | serialized.flip(); // for later reading 408 | ChromiumBase64EncodeByteBuffer(deserialized, serialized, serialized.limit()); 409 | System.out.println(new String(chars)); 410 | System.out.println(new String(deserialized)); 411 | } 412 | 413 | public static void main(String[] args) { 414 | byte[] chars = {'a', 'v', 'c', 'd', 'e', '0', '1', 'A', 'c', '2', '3', '4'}; 415 | TestRawArr(chars); 416 | TestByteBuffer(chars); 417 | } 418 | } 419 | --------------------------------------------------------------------------------