diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e0027a7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,134 @@
+# FireflyDB
+
+FireflyDB is a fast, thread-safe, JVM-based key-value storage engine with microsecond latency. FireflyDB is 20x faster
+for reads and 10x faster for writes than [Bitcask](https://github.com/basho/bitcask), which has a similar architecture.
+
+FireflyDB is hash-based and gives up range queries to achieve high throughput and low latency. As a result, it is about
+100x faster at writes than [LevelDB](https://github.com/google/leveldb) (Google)
+and [RocksDB](https://github.com/facebook/rocksdb) (Facebook).
+
+FireflyDB relies on sensible defaults and does not expose many configuration options. FireflyDB is designed with
+educated tradeoffs to achieve high performance:
+
+1. All the keys must fit in memory. This is a tradeoff with all hash-based storage engines. Even with the largest key
+ size of 32KB, FireflyDB can store 32,000+ keys per 1GB of memory.
+2. FireflyDB does not support range queries.
+3. Maximum key size is 32768 bytes or 32KB.
+4. Maximum value size is 2,147,483,647 bytes or 2.14 GB.
+
+## Installation
+
+### Maven
+
+```xml
+
+
+ com.sahilbondre
+ fireflydb
+ 0.1.0
+
+```
+
+### Gradle
+
+```gradle
+implementation 'com.sahilbondre:fireflydb:0.1.0'
+```
+
+## API
+
+```java
+FireflyDB fireflyDB=FireflyDB.getInstance("path/to/db");
+ fireflyDB.start();
+
+// Write
+ byte[]key="testKey".getBytes();
+ byte[]value="testValue".getBytes();
+
+ fireflyDB.put(key,value);
+
+// Read
+ byte[]result=fireflyDB.get(key);
+
+// Compaction
+// FireflyDB will compact automatically but can be triggered on demand.
+ fireflyDB.compact();
+
+ fireflyDB.stop();
+```
+
+## Benchmarks
+
+```
+iterations: 100,000
+cpu: 1
+memory: 1GB
+key-size: 8 bytes
+value-size: 100 bytes
+```
+
+### Random Write Test
+
+Test: Generate a random key and value and write it to the database.
+
+![write-test](./docs/write-test.png)
+
+| Database | Avg Time (microseconds) | P90 Latency (microseconds) |
+|-----------|-------------------------|----------------------------|
+| In-Memory | 0.53 | 1 |
+| LevelDB | 445.94 | 811 |
+| Bitcask | 71.33 | 48 |
+| RocksDB | 568.60 | 872 |
+| FireflyDB | 7.10 | 5 |
+
+### Random Read Test
+
+Test: Pick a random key from the ones written in the previous test and read it from the database.
+
+![read-test](./docs/read-test.png)
+
+| Database | Avg Time (microseconds) | P90 Latency (microseconds) |
+|-----------|-------------------------|----------------------------|
+| In-Memory | 0.49 | 1 |
+| LevelDB | 1.55 | 2 |
+| Bitcask | 108.03 | 62 |
+| RocksDB | 0.94 | 2 |
+| FireflyDB | 4.97 | 4 |
+
+### Alternating Read-Write Test
+
+Test: Perform a read and write operation alternately.
+
+![alternating-read-write-test](./docs/rw-test.png)
+
+| Database | Avg Time (microseconds) | P90 Latency (microseconds) |
+|-------------------|-------------------------|----------------------------|
+| In-Memory (read) | 0.61 | 1 |
+| In-Memory (write) | 0.57 | 1 |
+| LevelDB (read) | 3.43 | 5 |
+| LevelDB (write) | 441.38 | 814 |
+| Bitcask (read) | 120.15 | 60 |
+| Bitcask (write) | 66.78 | 57 |
+| RocksDB (read) | 4.54 | 7 |
+| RocksDB (write) | 567.14 | 971 |
+| FireflyDB (read) | 3.89 | 4 |
+| FireflyDB (write) | 3.91 | 4 |
+
+## Potential Improvements
+
+- [ ] Add an explicit delete operation.
+- [ ] Expose compaction size as a configuration option.
+- [ ] Expose compaction interval as a configuration option.
+- [ ] Allow larger key size as a configuration option.
+- [ ] Expose read only mode.
+
+## Contributing
+
+Pull requests are welcome. For major changes, please open an issue first
+to discuss what you would like to change.
+
+Please make sure to update tests as appropriate.
+
+## License
+
+[Apache 2.0](https://raw.githubusercontent.com/godcrampy/fireflydb/master/LICENSE)
diff --git a/benchmarks/firefly/.gitignore b/benchmarks/firefly/.gitignore
new file mode 100644
index 0000000..b425f09
--- /dev/null
+++ b/benchmarks/firefly/.gitignore
@@ -0,0 +1,35 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/benchmarks/firefly/Dockerfile b/benchmarks/firefly/Dockerfile
new file mode 100644
index 0000000..9bfa555
--- /dev/null
+++ b/benchmarks/firefly/Dockerfile
@@ -0,0 +1,7 @@
+FROM eclipse-temurin:17-jre-jammy
+
+WORKDIR /app/firefly
+
+COPY ./target/benchmark-1.0-SNAPSHOT-jar-with-dependencies.jar /app/firefly/
+
+CMD ["java", "-jar", "benchmark-1.0-SNAPSHOT-jar-with-dependencies.jar"]
diff --git a/benchmarks/firefly/build_image.sh b/benchmarks/firefly/build_image.sh
new file mode 100755
index 0000000..2756d9a
--- /dev/null
+++ b/benchmarks/firefly/build_image.sh
@@ -0,0 +1,3 @@
+mvn clean compile assembly:single
+
+docker build -t fireflydb-benchmark-firefly .
diff --git a/benchmarks/firefly/delete_image.sh b/benchmarks/firefly/delete_image.sh
new file mode 100644
index 0000000..df03b8d
--- /dev/null
+++ b/benchmarks/firefly/delete_image.sh
@@ -0,0 +1 @@
+docker rmi fireflydb-benchmark-firefly
diff --git a/benchmarks/firefly/docker-compose.yml b/benchmarks/firefly/docker-compose.yml
new file mode 100644
index 0000000..eac7b1b
--- /dev/null
+++ b/benchmarks/firefly/docker-compose.yml
@@ -0,0 +1,9 @@
+version: '3'
+services:
+ fireflydb-benchmark-bitcask:
+ image: fireflydb-benchmark-firefly
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: '1G'
diff --git a/benchmarks/firefly/pom.xml b/benchmarks/firefly/pom.xml
new file mode 100644
index 0000000..dac145e
--- /dev/null
+++ b/benchmarks/firefly/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+ com.sahilbondre.fireflydb
+ benchmark
+ 1.0-SNAPSHOT
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ com.sahilbondre
+ fireflydb
+ 0.1.0
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.6.0
+
+
+
+ com.sahilbondre.fireflydb.benchmark.Main
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/benchmarks/firefly/src/main/java/com/sahilbondre/fireflydb/benchmark/Main.java b/benchmarks/firefly/src/main/java/com/sahilbondre/fireflydb/benchmark/Main.java
new file mode 100644
index 0000000..96d20f5
--- /dev/null
+++ b/benchmarks/firefly/src/main/java/com/sahilbondre/fireflydb/benchmark/Main.java
@@ -0,0 +1,168 @@
+package com.sahilbondre.fireflydb.benchmark;
+
+import com.sahilbondre.firefly.FireflyDB;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Logger;
+
+public class Main {
+ private static final String TEST_FOLDER = "src/test/resources/test_folder";
+ private static final int ITERATIONS = 100000;
+ private static final int KEY_LENGTH = 8;
+ private static final int VALUE_LENGTH = 100;
+ private static final Logger logger = Logger.getLogger(Main.class.getName());
+
+ private static final FireflyDB fireflyDB = FireflyDB.getInstance(TEST_FOLDER);
+
+
+
+ public static void main(String[] args) throws IOException {
+ Files.createDirectories(Paths.get(TEST_FOLDER));
+
+ fireflyDB.start();
+
+ logger.info("Starting benchmark...");
+ logger.info("Iterations: " + ITERATIONS);
+ logger.info("Key length: " + KEY_LENGTH);
+ logger.info("Value length: " + VALUE_LENGTH);
+
+ logger.info("Starting writes...");
+
+ long[] writeTimes = new long[ITERATIONS];
+
+ List availableKeys = new ArrayList<>();
+
+ long startTime = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ byte[] key = getRandomBytes(KEY_LENGTH);
+ byte[] value = getRandomBytes(VALUE_LENGTH);
+
+ long writeTime = saveKeyValuePairAndGetTime(key, value);
+ availableKeys.add(key);
+ writeTimes[i] = writeTime;
+ }
+ long totalTime = (System.nanoTime() - startTime) / 1000; // Convert nanoseconds to microseconds
+ logger.info("Total time for writes: " + totalTime + " mus");
+
+ double averageWriteTime = 0;
+ for (long writeTime : writeTimes) {
+ averageWriteTime += writeTime;
+ }
+ averageWriteTime /= ITERATIONS;
+
+ logger.info("Average write latency: " + averageWriteTime + " mus");
+
+ // Calculate p90 write latency
+ // Sort write times
+ Arrays.sort(writeTimes);
+ long p90WriteTime = writeTimes[(int) (ITERATIONS * 0.9)];
+ logger.info("p90 write latency: " + p90WriteTime + " mus");
+
+ // Benchmark reads
+ logger.info("\nStarting reads...");
+
+ long[] readTimes = new long[ITERATIONS];
+
+ startTime = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ // Get a random key from the list of available keys
+ byte[] key = availableKeys.get(new Random().nextInt(availableKeys.size()));
+
+ long readTime = getKeyValuePairAndGetTime(key);
+ readTimes[i] = readTime;
+ }
+ totalTime = (System.nanoTime() - startTime) / 1000; // Convert nanoseconds to microseconds
+ logger.info("Total time for reads: " + totalTime + " mus");
+
+ double averageReadTime = 0;
+ for (long readTime : readTimes) {
+ averageReadTime += readTime;
+ }
+ averageReadTime /= ITERATIONS;
+
+ logger.info("Average read latency: " + averageReadTime + " mus");
+
+ // Calculate p90 read latency
+ // Sort read times
+ Arrays.sort(readTimes);
+ long p90ReadTime = readTimes[(int) (ITERATIONS * 0.9)];
+ logger.info("p90 read latency: " + p90ReadTime + " mus");
+
+
+ // Benchmark reads and writes
+ logger.info("\nStarting reads and writes...");
+
+
+ startTime = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ byte[] writeKey = getRandomBytes(KEY_LENGTH);
+ byte[] writeValue = getRandomBytes(VALUE_LENGTH);
+
+ long writeTime = saveKeyValuePairAndGetTime(writeKey, writeValue);
+
+ availableKeys.add(writeKey);
+
+ byte[] readKey = availableKeys.get(new Random().nextInt(availableKeys.size()));
+
+ long readTime = getKeyValuePairAndGetTime(readKey);
+
+ writeTimes[i] = writeTime;
+ readTimes[i] = readTime;
+ }
+ totalTime = (System.nanoTime() - startTime) / 1000; // Convert nanoseconds to microseconds
+ logger.info("Total time for reads and writes: " + totalTime + " mus");
+
+ averageReadTime = 0;
+ for (long readTime : readTimes) {
+ averageReadTime += readTime;
+ }
+ averageReadTime /= ITERATIONS;
+
+ averageWriteTime = 0;
+ for (long writeTime : writeTimes) {
+ averageWriteTime += writeTime;
+ }
+ averageWriteTime /= ITERATIONS;
+
+ logger.info("Average read latency: " + averageReadTime + " mus");
+ logger.info("Average write latency: " + averageWriteTime + " mus");
+
+ // Calculate p90 read latency
+ // Sort read times
+ Arrays.sort(readTimes);
+
+ // Calculate p90 write latency
+ // Sort write times
+ Arrays.sort(writeTimes);
+
+ p90ReadTime = readTimes[(int) (ITERATIONS * 0.9)];
+ logger.info("p90 read latency: " + p90ReadTime + " mus");
+
+ p90WriteTime = writeTimes[(int) (ITERATIONS * 0.9)];
+ logger.info("p90 write latency: " + p90WriteTime + " mus");
+ }
+
+ private static byte[] getRandomBytes(int length) {
+ byte[] bytes = new byte[length];
+ new Random().nextBytes(bytes);
+ return bytes;
+ }
+
+ private static long saveKeyValuePairAndGetTime(byte[] key, byte[] value) throws IOException {
+ long startTime = System.nanoTime();
+ fireflyDB.set(key, value);
+ return (System.nanoTime() - startTime) / 1000; // Convert nanoseconds to microseconds
+ }
+
+ private static long getKeyValuePairAndGetTime(byte[] key) throws IOException {
+ long startTime = System.nanoTime();
+ fireflyDB.get(key);
+ return (System.nanoTime() - startTime) / 1000; // Convert nanoseconds to microseconds
+ }
+}
diff --git a/docs/read-test.png b/docs/read-test.png
new file mode 100644
index 0000000..71e4207
Binary files /dev/null and b/docs/read-test.png differ
diff --git a/docs/rw-test.png b/docs/rw-test.png
new file mode 100644
index 0000000..3dad256
Binary files /dev/null and b/docs/rw-test.png differ
diff --git a/docs/write-test.png b/docs/write-test.png
new file mode 100644
index 0000000..4d9d9af
Binary files /dev/null and b/docs/write-test.png differ