Compare commits

..

10 commits

Author SHA1 Message Date
Shautvast
3e63eef1d1 removed souts 2023-08-23 09:28:49 +02:00
Shautvast
178977d3b9 replaced MethodHandles with custom reflection-less reflectivity 2023-08-23 09:21:25 +02:00
Shautvast
7d7e158b6b replaced MethodHandles with custom reflection-less reflectivity 2023-08-23 09:16:46 +02:00
Shautvast
209e43657d replaced MethodHandles with custom reflection-less reflectivity 2023-08-23 09:15:37 +02:00
Shautvast
938f6e9521 replaced MethodHandles with custom reflection-less reflectivity 2023-08-23 09:14:09 +02:00
Shautvast
d7d4ca06fc added benchmark results 2023-08-22 15:11:36 +02:00
Shautvast
ccb0bbf662 added benchmark results 2023-08-22 15:10:12 +02:00
Shautvast
f90102d57f adjusted readme 2023-08-22 14:49:09 +02:00
Shautvast
0bd9f62be0 adjusted readme 2023-08-22 14:44:55 +02:00
Shautvast
f7f999bd53 added benchmark and renamed packages 2023-08-22 14:38:38 +02:00
56 changed files with 984 additions and 198 deletions

View file

@ -1,11 +1,45 @@
**Design decisions** **Design decisions**
* built for speed and efficiency (within java) * built for speed and efficiency (within java)
* uses storage format borrowed from SQLite with 1 exception: float is stored as f32 (SQLite only uses f64) * uses storage format borrowed from SQLite with 1 exception: float is stored as f32 (SQLite only uses f64)
* needs jdk 9 (VarHandles) * needs jdk 9 (VarHandles)
* minimal reflection (once per creation of the list) * minimal reflection (once per creation of the list)
* The only class (for now): [ContiguousList](https://github.com/shautvast/Contiguous/blob/main/src/main/java/nl/sanderhautvast/contiguous/ContiguousList.java) * The only class (for
now): [ContiguousList](https://github.com/shautvast/Contiguous/blob/main/lib/src/main/java/com/github/shautvast/contiguous/ContiguousList.java)
* Still in a very early stage * Still in a very early stage
* the code is working * the code is working
* but it remains to be seen if this is a good idea * but it remains to be seen if this is a good idea
**JMH Benchmark**
| Benchmark | Mode | Cnt | Score | Error | Units |
|------------|------|----:|-------------:|-------------:|-------|
| classic | avgt | 5 | 13312478.471 | ± 259931.663 | ns/op |
| contiguous | avgt | 5 | 13063782.511 | ± 451500.662 | ns/op |
* pretty much on par despite all the overhead for inspecting the list elements
* will try to squeeze out more performance
*Benchmark environment details*
```
[ec2-user@ip-172-31-22-215 Contiguous]$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 46 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 1
On-line CPU(s) list: 0
Vendor ID: GenuineIntel
Model name: Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz
CPU family: 6
Model: 63
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 1
Stepping: 2
BogoMIPS: 4800.04
```

102
benchmark/pom.xml Normal file
View file

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.shautvast</groupId>
<artifactId>contiguous-pom</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>benchmark</artifactId>
<properties>
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.github.shautvast</groupId>
<artifactId>contiguous-jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.shautvast</groupId>
<artifactId>contiguous-jackson</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.36</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<compilerVersion>9</compilerVersion>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>benchmark</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,20 @@
package com.github.shautvast.contiguous.benchmark;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/* copied from demo, need common module */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
String name;
String email;
String streetname;
int housenumber;
String city;
String country;
}

View file

@ -0,0 +1,63 @@
package com.github.shautvast.contiguous.benchmark;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.github.shautvast.contiguous.ContiguousList;
import com.github.shautvast.contiguous.ListSerializer;
import org.openjdk.jmh.annotations.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1)
public class JmhBenchmark {
@org.openjdk.jmh.annotations.State(Scope.Benchmark)
public static class State {
//mimick database
final List<Customer> customers = new ArrayList<>();
final ObjectMapper jacksonMapper = new ObjectMapper();
@Setup()
public void setup() {
final SimpleModule contiguousListModule = new SimpleModule("contiguous_module");
contiguousListModule.addSerializer(new ListSerializer());
jacksonMapper.registerModule(contiguousListModule);
final RandomStuffGenerator generator = new RandomStuffGenerator();
for (int i = 0; i < 10_000; i++) {
Customer customer = new Customer();
String firstName = generator.generateFirstName();
String lastName = generator.generateLastName();
customer.name = firstName + " " + lastName;
customer.email = firstName + "." + lastName + "@icemail.com";
customer.streetname = generator.generateStreetName();
customer.housenumber = generator.generateSomeNumber();
customer.city = generator.generateSomeCityInIceland();
customer.country = generator.generateIceland();
customers.add(customer);
}
}
}
@Benchmark
public String contiguous(State state) throws JsonProcessingException {
//naive mimick read from database and add to ContiguousList
ContiguousList<Customer> customers = new ContiguousList<>(Customer.class);
customers.addAll(state.customers);
return state.jacksonMapper.writeValueAsString(customers);
}
@Benchmark
public String classic(State state) throws JsonProcessingException {
//naive mimick read from database and add to ArrayList
List<Customer> customers = new ArrayList<>();
customers.addAll(state.customers);
return state.jacksonMapper.writeValueAsString(customers);
}
}

View file

@ -0,0 +1,55 @@
package com.github.shautvast.contiguous.benchmark;
import java.util.List;
import java.util.Random;
/* Duplicate from demo */
public class RandomStuffGenerator {
private final List<String> firstNameParts = List.of("sa", "ka", "zo", "ja", "za", "ka", "po", "ji", "ne", "si", "wi", "ha", "ut", "va", "no", "bo"
, "jo", "fe", "gu");
private final List<String> lastNameParts = List.of("fin", "wil", "cat", "loc", "der", "ter", "asp", "pen", "ill", "raf", "gut", "dax", "yin");
private final List<String> cities = List.of("Reykjavík", "Kópavogur", "Hafnarfjörður", "Akureyri", "Reykjanesbær", "Garðabær", "Mosfellsbær", "Selfoss", "Akranes", "Seltjarnarnes", "Vestmannaeyjar", "Grindavík", "Ísafjörður", "Álftanes", "Sauðárkrókur", "Hveragerði", "Egilsstaðir", "Húsavík", "Borgarnes", "Sandgerði", "Höfn", "Þorlákshöfn", "Garður", "Neskaupstaður", "Dalvík", "Reyðarfjörður", "Siglufjörður", "Vogar", "Stykkishólmur", "Eskifjörður", "Ólafsvík", "Hvolsvöllur", "Bolungarvík", "Hella", "Grundarfjörður", "Blönduós", "Ólafsfjörður", "Fáskrúðsfjörður", "Patreksfjörður", "Seyðisfjörður", "Grundarhverfi", "Hvammstangi", "Stokkseyri", "Eyrarbakki", "Vopnafjörður", "Skagaströnd", "Flúðir", "Vík", "Fellabær", "Hellissandur", "Djúpivogur", "Þórshöfn", "Svalbarðseyri", "Hólmavík", "Grenivík", "Hvanneyri", "Þingeyri", "Búðardalur", "Reykholt", "Hrafnagil", "Suðureyri", "Tálknafjörður", "Bíldudalur", "Mosfellsdalur", "Hnífsdalur", "Reykjahlíð", "Laugarvatn", "Raufarhöfn", "Stöðvarfjörður", "Bifröst", "Flateyri", "Kirkjubæjarklaustur", "Súðavík", "Hrísey", "Hofsós", "Breiðdalsvík", "Rif", "Reykhólar", "Varmahlíð", "Kópasker", "Laugarás", "Borg", "Hauganes", "Hafnir", "Laugar", "Melahverfi", "Tjarnabyggð", "Árskógssandur", "Lónsbakki", "Hólar", "Nesjahverfi", "Sólheimar", "Brúnahlíð", "Drangsnes", "Borgarfjörður eystri", "Árbæjarhverfi", "Brautarholt", "Rauðalækur", "Bakkafjörður", "Innnes", "Grímsey", "Þykkvabær", "Laugarbakki", "Reykholt", "Árnes", "Kristnes", "Kleppjárnsreykir");
private final Random random = new Random();
public String generateFirstName() {
return generateName(firstNameParts);
}
public String generateLastName() {
return generateName(lastNameParts);
}
public String generateStreetName() {
StringBuilder name = new StringBuilder();
int nLastNameParts = random.nextInt(5) + 1;
for (int i = 0; i < nLastNameParts; i++) {
name.append(firstNameParts.get(random.nextInt(firstNameParts.size())));
name.append(lastNameParts.get(random.nextInt(lastNameParts.size())));
}
name.append("götu");
return name.toString();
}
public int generateSomeNumber() {
return random.nextInt(1000);
}
public String generateSomeCityInIceland() {
return cities.get(random.nextInt(cities.size()));
}
public String generateIceland() {
return "Iceland"; // meant to be humorous
}
private String generateName(List<String> parts) {
StringBuilder name = new StringBuilder();
int size = random.nextInt(2) + 2;
for (int i = 0; i < size; i++) {
name.append(parts.get(random.nextInt(parts.size())));
}
return name.toString();
}
}

View file

@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous-pom</artifactId> <artifactId>contiguous-pom</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</parent> </parent>
@ -21,12 +21,12 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous-jdbc</artifactId> <artifactId>contiguous-jdbc</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous-jackson</artifactId> <artifactId>contiguous-jackson</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
@ -66,7 +66,8 @@
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.12</version> <version>2.7.12</version>
<configuration> <configuration>
<mainClass>nl.sanderhautvast.contiguous.demo.DemoApplication</mainClass> <mainClass>com.github.shautvast.contiguous.demo.DemoApplication</mainClass>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View file

@ -1,10 +1,10 @@
package nl.sanderhautvast.contiguous.demo; package com.github.shautvast.contiguous.demo;
import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import nl.sanderhautvast.contiguous.ListSerializer; import com.github.shautvast.contiguous.ListSerializer;
import nl.sanderhautvast.contiguous.demo.repository.RandomStuffGenerator; import com.github.shautvast.contiguous.demo.repository.RandomStuffGenerator;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous.demo.model; package com.github.shautvast.contiguous.demo.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;

View file

@ -1,10 +1,10 @@
package nl.sanderhautvast.contiguous.demo.repository; package com.github.shautvast.contiguous.demo.repository;
import com.github.shautvast.contiguous.ContiguousList;
import com.github.shautvast.contiguous.JdbcResults;
import com.github.shautvast.contiguous.demo.model.Customer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import nl.sanderhautvast.contiguous.ContiguousList;
import nl.sanderhautvast.contiguous.JdbcResults;
import nl.sanderhautvast.contiguous.demo.model.Customer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -29,7 +29,7 @@ public class CustomerRepository {
} }
public List<Customer> getAllCustomersTraditional() { public List<Customer> getAllCustomersTraditional() {
return jdbcTemplate.query("select * from customers", (rs, rowNum) -> new Customer( return jdbcTemplate.query("select * from customers limit 10000", (rs, rowNum) -> new Customer(
rs.getString("name"),rs.getString("email"), rs.getString("name"),rs.getString("email"),
rs.getString("streetname"), rs.getInt("housenumber"), rs.getString("streetname"), rs.getInt("housenumber"),
rs.getString("city"), rs.getString("country") rs.getString("city"), rs.getString("country")

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous.demo.repository; package com.github.shautvast.contiguous.demo.repository;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;

View file

@ -1,9 +1,9 @@
package nl.sanderhautvast.contiguous.demo.rest; package com.github.shautvast.contiguous.demo.rest;
import com.github.shautvast.contiguous.ContiguousList;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import nl.sanderhautvast.contiguous.ContiguousList; import com.github.shautvast.contiguous.demo.model.Customer;
import nl.sanderhautvast.contiguous.demo.model.Customer; import com.github.shautvast.contiguous.demo.repository.CustomerRepository;
import nl.sanderhautvast.contiguous.demo.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -23,7 +23,12 @@ public class DemoRestApi {
@GetMapping(value = "/api/customers", produces = "application/json") @GetMapping(value = "/api/customers", produces = "application/json")
public ContiguousList<Customer> getCustomers() { public ContiguousList<Customer> getCustomers() {
try {
return customerRepository.getAllCustomers(); return customerRepository.getAllCustomers();
} catch (Exception e) {
log.error("",e);
throw e;
}
} }
@GetMapping(value = "/api/customers/traditional", produces = "application/json") @GetMapping(value = "/api/customers/traditional", produces = "application/json")
@ -35,4 +40,5 @@ public class DemoRestApi {
public List<Customer> getCustomersHybrid() { public List<Customer> getCustomersHybrid() {
return customerRepository.getAllCustomersHybrid(); return customerRepository.getAllCustomersHybrid();
} }
} }

View file

@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous-pom</artifactId> <artifactId>contiguous-pom</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</parent> </parent>
@ -23,7 +23,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous</artifactId> <artifactId>contiguous</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
@ -7,9 +7,9 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
public class ListSerializer<E> extends StdSerializer<ContiguousList<?>> { public class ListSerializer extends StdSerializer<ContiguousList<?>> {
@SuppressWarnings("unchecked") @SuppressWarnings({"unchecked", "rawtypes"})
public ListSerializer() { public ListSerializer() {
super((Class) ContiguousList.class); // ? super((Class) ContiguousList.class); // ?
} }
@ -22,10 +22,12 @@ public class ListSerializer<E> extends StdSerializer<ContiguousList<?>> {
Iterator<String> jsons = clist.jsonIterator(); Iterator<String> jsons = clist.jsonIterator();
while (jsons.hasNext()) { while (jsons.hasNext()) {
generator.writeRawValue(jsons.next()); String next = jsons.next();
generator.writeRawValue(next);
} }
generator.writeEndArray(); generator.writeEndArray();
clist.close();
} }
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
public class AdamsObject { public class AdamsObject {
private String name; private String name;

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -10,16 +10,16 @@ import static org.junit.jupiter.api.Assertions.*;
class ListSerializerTest { class ListSerializerTest {
private ObjectMapper mapper; private ObjectMapper mapper;
@BeforeEach @BeforeEach
public void setup(){ public void setup() {
mapper = new ObjectMapper(); mapper = new ObjectMapper();
final SimpleModule module = new SimpleModule("mySerializers"); final SimpleModule module = new SimpleModule("mySerializers");
module.addSerializer(new ListSerializer<>()); module.addSerializer(new ListSerializer());
mapper.registerModule(module); mapper.registerModule(module);
} }
@Test @Test
public void testStringList() throws JsonProcessingException { public void testStringList() throws JsonProcessingException {
ContiguousList<String> strings = new ContiguousList<>(String.class); ContiguousList<String> strings = new ContiguousList<>(String.class);
@ -42,7 +42,6 @@ class ListSerializerTest {
strings.add(new AdamsObject("Publishing houses of Ursa Minor")); strings.add(new AdamsObject("Publishing houses of Ursa Minor"));
String json = mapper.writeValueAsString(strings); String json = mapper.writeValueAsString(strings);
assertEquals("[{\"name\": \"Vogon constructor fleet\"}," + assertEquals("[{\"name\": \"Vogon constructor fleet\"}," +
"{\"name\": \"Restaurant at the end of the Galaxy\"}," + "{\"name\": \"Restaurant at the end of the Galaxy\"}," +

View file

@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous-pom</artifactId> <artifactId>contiguous-pom</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</parent> </parent>
@ -23,7 +23,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous</artifactId> <artifactId>contiguous</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;

View file

@ -1,27 +1,22 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class JdbcResultsTest { class JdbcResultsTest {
@Mock
private ResultSet mockResults;
@Test @Test
public void testListOfString() throws SQLException { public void testListOfString() throws SQLException {
ResultSet mockResults = Mockito.mock(ResultSet.class);
when(mockResults.next()).thenReturn(true, false); when(mockResults.next()).thenReturn(true, false);
when(mockResults.getObject(1)).thenReturn("Zaphod"); when(mockResults.getObject(1)).thenReturn("Zaphod");
@ -36,6 +31,7 @@ class JdbcResultsTest {
@Test @Test
public void testListOfBean() throws SQLException { public void testListOfBean() throws SQLException {
ResultSet mockResults = Mockito.mock(ResultSet.class);
when(mockResults.next()).thenReturn(true, false); when(mockResults.next()).thenReturn(true, false);
// the shape of the result equals that of the result (name:String, age:int) // the shape of the result equals that of the result (name:String, age:int)
when(mockResults.getObject("name")).thenReturn("Zaphod"); when(mockResults.getObject("name")).thenReturn("Zaphod");
@ -52,6 +48,7 @@ class JdbcResultsTest {
@Test @Test
public void testNameMapping() throws SQLException { public void testNameMapping() throws SQLException {
ResultSet mockResults = Mockito.mock(ResultSet.class);
when(mockResults.next()).thenReturn(true, false); when(mockResults.next()).thenReturn(true, false);
when(mockResults.getObject("name")).thenReturn("Trillian"); when(mockResults.getObject("name")).thenReturn("Trillian");
when(mockResults.getObject("realName")).thenReturn("Tricia MacMillan"); when(mockResults.getObject("realName")).thenReturn("Tricia MacMillan");

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View file

@ -16,17 +16,17 @@
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true"> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp> <boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">100</stringProp> <stringProp name="LoopController.loops">1000</stringProp>
</elementProp> </elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp> <stringProp name="ThreadGroup.num_threads">3</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp> <stringProp name="ThreadGroup.ramp_time">10</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp> <boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp> <stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp> <stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp> <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup> </ThreadGroup>
<hashTree> <hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="contiguous" enabled="false"> <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="contiguous" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/> <collectionProp name="Arguments.arguments"/>
</elementProp> </elementProp>
@ -34,7 +34,7 @@
<stringProp name="HTTPSampler.port">8080</stringProp> <stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp> <stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp> <stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/api/customers</stringProp> <stringProp name="HTTPSampler.path">/api/customers/streamon</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
@ -45,7 +45,7 @@
<stringProp name="HTTPSampler.response_timeout"></stringProp> <stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy> </HTTPSamplerProxy>
<hashTree/> <hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="traditional" enabled="true"> <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="traditional" enabled="false">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/> <collectionProp name="Arguments.arguments"/>
</elementProp> </elementProp>
@ -83,6 +83,49 @@
<stringProp name="HTTPSampler.response_timeout"></stringProp> <stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy> </HTTPSamplerProxy>
<hashTree/> <hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="toy" enabled="false">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">api/customers/toy</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="Uniform Random Timer" enabled="true">
<stringProp name="ConstantTimer.delay">0</stringProp>
<stringProp name="RandomTimer.range">100.0</stringProp>
</UniformRandomTimer>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="streamon" enabled="false">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/api/customers/streamon2</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
</hashTree> </hashTree>
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true"> <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp> <boolProp name="ResultCollector.error_logging">false</boolProp>

View file

@ -5,22 +5,27 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous-pom</artifactId> <artifactId>contiguous-pom</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>contiguous</artifactId> <artifactId>contiguous</artifactId>
<description>Datastructures with contiguous storage. Core library with no external dependencies</description> <description>Datastructures with contiguous storage. Core library</description>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<maven.compiler.source>9</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>9</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>com.github.shautvast</groupId>
<artifactId>reflective</artifactId>
<version>1.2.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
@ -40,4 +45,12 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub OWNER Apache Maven Packages</name>
<url>https://maven.pkg.github.com/shautvast/Contiguous</url>
</repository>
</distributionManagement>
</project> </project>

View file

@ -1,10 +1,11 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import com.github.shautvast.reflective.MetaMethod;
import java.lang.invoke.MethodHandle;
import java.math.BigDecimal; import java.math.BigDecimal;
class BigDecimalHandler extends BuiltinTypeHandler<BigDecimal> { class BigDecimalHandler extends BuiltinTypeHandler<BigDecimal> {
public BigDecimalHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public BigDecimalHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(BigDecimal.class, propertyName, getter, setter); super(BigDecimal.class, propertyName, getter, setter);
} }

View file

@ -1,10 +1,11 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import com.github.shautvast.reflective.MetaMethod;
import java.lang.invoke.MethodHandle;
import java.math.BigInteger; import java.math.BigInteger;
class BigIntegerHandler extends BuiltinTypeHandler<BigInteger> { class BigIntegerHandler extends BuiltinTypeHandler<BigInteger> {
public BigIntegerHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public BigIntegerHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(BigInteger.class, propertyName, getter, setter); super(BigInteger.class, propertyName, getter, setter);
} }

View file

@ -0,0 +1,22 @@
package com.github.shautvast.contiguous;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* Boundless cache something
*/
class BufferCache {
private static final ConcurrentHashMap<Integer, ByteBuffer> cache = new ConcurrentHashMap<>();
static ByteBuffer get(int size) {
ByteBuffer byteBuffer = Optional.ofNullable(cache.get(size)).orElseGet(() -> ByteBuffer.allocate(size));
byteBuffer.position(0);
return byteBuffer;
}
static void release(ByteBuffer byteBuffer) {
cache.put(byteBuffer.capacity(), byteBuffer);
}
}

View file

@ -1,6 +1,6 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
/** /**
* Base class for handlers. Its responsibility is to read and write a property from the incoming object to the internal storage. * Base class for handlers. Its responsibility is to read and write a property from the incoming object to the internal storage.
@ -11,8 +11,8 @@ import java.lang.invoke.MethodHandle;
* of the bean that it needs to call 'runtime' (after instantiation of the list), * of the bean that it needs to call 'runtime' (after instantiation of the list),
* ie. when a bean is added or retrieved from the list * ie. when a bean is added or retrieved from the list
*/ */
public abstract class BuiltinTypeHandler<T> extends TypeHandler { abstract class BuiltinTypeHandler<T> extends TypeHandler {
public BuiltinTypeHandler(Class<?> type, String name, MethodHandle getter, MethodHandle setter) { public BuiltinTypeHandler(Class<?> type, String name, MetaMethod getter, MetaMethod setter) {
super(type, name, getter, setter); super(type, name, getter, setter);
} }
@ -29,18 +29,19 @@ public abstract class BuiltinTypeHandler<T> extends TypeHandler {
store(propertyValue, typedList); store(propertyValue, typedList);
} }
@SuppressWarnings("unchecked")
void storeValue(Object value, ContiguousList<?> contiguousList) { void storeValue(Object value, ContiguousList<?> contiguousList) {
store((T) value, contiguousList); store((T) value, contiguousList);
} }
@SuppressWarnings("unchecked")
private T getValue(Object propertyValue) { private T getValue(Object propertyValue) {
// I don't trust this // I don't trust this
if (getter == null) { if (getter == null) {
return (T) propertyValue; return (T) propertyValue;
} }
try { try {
return (T) getter.invoke(propertyValue); return (T) getter.invoke(propertyValue).unwrap();
} catch (Throwable e) { } catch (Throwable e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
@ -58,7 +59,7 @@ public abstract class BuiltinTypeHandler<T> extends TypeHandler {
*/ */
public void setValue(Object instance, Object value) { public void setValue(Object instance, Object value) {
try { try {
setter.invokeWithArguments(instance, cast(value)); setter.invoke(instance, cast(value)).unwrap();
} catch (Throwable e) { } catch (Throwable e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }

View file

@ -1,13 +1,13 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
/** /**
* Stores a byte value. * Stores a byte value.
*/ */
class ByteHandler extends BuiltinTypeHandler<Byte> { class ByteHandler extends BuiltinTypeHandler<Byte> {
public ByteHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public ByteHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(Byte.class, propertyName, getter, setter); super(Byte.class, propertyName, getter, setter);
} }

View file

@ -1,7 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.*; import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
class CompoundTypeHandler extends TypeHandler { class CompoundTypeHandler extends TypeHandler {
private final Map<String, TypeHandler> properties = new LinkedHashMap<>(); private final Map<String, TypeHandler> properties = new LinkedHashMap<>();

View file

@ -1,7 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import com.github.shautvast.reflective.MetaClass;
import com.github.shautvast.reflective.MetaMethod;
import com.github.shautvast.reflective.Reflective;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -56,22 +58,27 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
/* /*
* storage for dehydated objects * storage for dehydated objects
*/ */
private ByteBuffer data = ByteBuffer.allocate(4096);//TODO create constructor with capacity private ByteBuffer data = BufferCache.get(4096);
private int bufferPosition; private int bufferPosition;
private ArrayList<Integer> elementIndices = new ArrayList<>(); // avoids autoboxing. Could also use standard ArrayList though private final ArrayList<Integer> elementIndices = new ArrayList<>(); // avoids autoboxing. Could also use standard ArrayList though
// is there a standard lib IntList?? // is there a standard lib IntList??
private int size; private int size;
private TypeHandler rootHandler; private final TypeHandler rootHandler;
private static final Map<Class<?>, TypeHandler> TYPE_HANDLERS = new HashMap<>();
public ContiguousList(Class<E> type) { public ContiguousList(Class<E> type) {
inspectType(type); this.rootHandler = inspectType(type);
elementIndices.add(0); // index of first element elementIndices.add(0); // index of first element
} }
public void close() {
BufferCache.release(this.data);
}
/* /*
* Get a list of setters and getters to execute later on to get/set the values of the object * Get a list of setters and getters to execute later on to get/set the values of the object
* *
@ -80,44 +87,41 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
* The advantage of the current implementation is that the binary data is not aware of the actual * The advantage of the current implementation is that the binary data is not aware of the actual
* object graph. It only knows the 'primitive' values. * object graph. It only knows the 'primitive' values.
*/ */
private void inspectType(Class<?> elementClass) { private TypeHandler inspectType(Class<?> elementClass) {
if (PropertyHandlerFactory.isBuiltInType(elementClass)) { return TYPE_HANDLERS.computeIfAbsent(elementClass, k ->
this.rootHandler = PropertyHandlerFactory.forType(elementClass); PropertyHandlerFactory.forType(elementClass)
} else { .orElseGet(() -> {
CompoundTypeHandler compoundType = new CompoundTypeHandler(elementClass); TypeHandler compoundTypeHandler = new CompoundTypeHandler(elementClass);
this.rootHandler = compoundType; addPropertyHandlersForCompoundType(elementClass, (CompoundTypeHandler) compoundTypeHandler);
try { return compoundTypeHandler;
addPropertyHandlersForCompoundType(elementClass, compoundType); })
} catch (IllegalAccessException e) { );
throw new IllegalStateException(e);
}
}
} }
/* /*
* using reflection find all properties in the element, recursing down when the property is compound * using reflection find all properties in the element, recursing down when the property is compound
*/ */
private void addPropertyHandlersForCompoundType(Class<?> type, CompoundTypeHandler parentCompoundType) throws IllegalAccessException { private void addPropertyHandlersForCompoundType(Class<?> type, CompoundTypeHandler parentCompoundType) {
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup()); MetaClass metaType = Reflective.getMetaClass(type);
Arrays.stream(type.getDeclaredFields()) Arrays.stream(type.getDeclaredFields())
.forEach(field -> { .forEach(field -> {
try { try {
Class<?> fieldType = field.getType(); Class<?> fieldType = field.getType();
MethodHandle getter = lookup.findGetter(type, field.getName(), fieldType);
MethodHandle setter = lookup.findSetter(type, field.getName(), fieldType);
if (PropertyHandlerFactory.isBuiltInType(fieldType)) { String capitalized = capitalize(field.getName());
BuiltinTypeHandler<?> primitiveType = MetaMethod getter = metaType.getMethod("get" + capitalized).get();
PropertyHandlerFactory.forType(fieldType, field.getName(), getter, setter); MetaMethod setter = metaType.getMethod("set" + capitalized).get();
parentCompoundType.addHandler(field.getName(), primitiveType); Optional<TypeHandler> typeHandler = PropertyHandlerFactory.forType(fieldType, field.getName(), getter, setter);
if (typeHandler.isPresent()) {
parentCompoundType.addHandler(field.getName(), (BuiltinTypeHandler<?>) typeHandler.get());
} else { } else {
CompoundTypeHandler newParent = new CompoundTypeHandler(fieldType, field.getName()); CompoundTypeHandler handler = new CompoundTypeHandler(fieldType, field.getName());
newParent.setGetter(getter); handler.setGetter(getter);
newParent.setSetter(setter); handler.setSetter(setter);
parentCompoundType.addChild(field, newParent); parentCompoundType.addChild(field, handler);
addPropertyHandlersForCompoundType(fieldType, newParent); addPropertyHandlersForCompoundType(fieldType, handler);
} }
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -153,7 +157,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
} else { } else {
CompoundTypeHandler child = ((CompoundTypeHandler) property); CompoundTypeHandler child = ((CompoundTypeHandler) property);
try { try {
Object result = child.getGetter().invoke(element); Object result = child.getGetter().invoke(element).unwrap();
storePropertyData(result, child); storePropertyData(result, child);
} catch (Throwable e) { } catch (Throwable e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -205,7 +209,8 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
compoundType.getProperties().forEach(property -> { compoundType.getProperties().forEach(property -> {
if (property instanceof BuiltinTypeHandler) { if (property instanceof BuiltinTypeHandler) {
BuiltinTypeHandler<?> type = ((BuiltinTypeHandler<?>) property); BuiltinTypeHandler<?> type = ((BuiltinTypeHandler<?>) property);
type.setValue(element, ValueReader.read(data)); Object readValue = ValueReader.read(data);
type.setValue(element, readValue);
} else { } else {
try { try {
CompoundTypeHandler p = (CompoundTypeHandler) property; CompoundTypeHandler p = (CompoundTypeHandler) property;
@ -213,7 +218,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
Object newInstance = p.getType().getDeclaredConstructor().newInstance(); Object newInstance = p.getType().getDeclaredConstructor().newInstance();
// set it on the parent // set it on the parent
p.getSetter().invokeWithArguments(element, newInstance); p.getSetter().invoke(element, newInstance);
// recurse down // recurse down
copyDataIntoNewObject(newInstance, p); copyDataIntoNewObject(newInstance, p);
@ -270,7 +275,6 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
} }
data.position(elementIndices.get(index)); data.position(elementIndices.get(index));
if (rootHandler instanceof BuiltinTypeHandler<?>) { if (rootHandler instanceof BuiltinTypeHandler<?>) {
BuiltinTypeHandler<?> handler = (BuiltinTypeHandler<?>) rootHandler; BuiltinTypeHandler<?> handler = (BuiltinTypeHandler<?>) rootHandler;
return getValue(handler); return getValue(handler);
} }
@ -280,22 +284,19 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
copyDataIntoStringBuilder(s, (CompoundTypeHandler) rootHandler); copyDataIntoStringBuilder(s, (CompoundTypeHandler) rootHandler);
s.append("}"); s.append("}");
return s.toString(); return s.toString();
} }
private String getValue(BuiltinTypeHandler<?> handler) { private String getValue(BuiltinTypeHandler<?> handler) {
Object read = ValueReader.read(data); String value = String.valueOf(ValueReader.read(data));
String out = handler.cast(read).toString();
if (handler instanceof StringHandler) { if (handler instanceof StringHandler) {
out = quote(out); return quote(value);
} }
return out; return value;
} }
private static String quote(String out) { private static String quote(String out) {
StringBuilder s = new StringBuilder(out.length() + 2); StringBuilder s = new StringBuilder(out.length() + 2);
out = s.append("\"").append(out).append("\"").toString(); return s.append("\"").append(out).append("\"").toString();
return out;
} }
/* /*
@ -305,8 +306,9 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
compoundType.getProperties().forEach(property -> { compoundType.getProperties().forEach(property -> {
if (property instanceof BuiltinTypeHandler) { if (property instanceof BuiltinTypeHandler) {
BuiltinTypeHandler<?> typeHandler = (BuiltinTypeHandler<?>) property; BuiltinTypeHandler<?> typeHandler = (BuiltinTypeHandler<?>) property;
s.append(quote(typeHandler.getName())) s.append("\"")
.append(": ") .append(typeHandler.getName())
.append("\": ")
.append(getValue(typeHandler)); .append(getValue(typeHandler));
} else { } else {
CompoundTypeHandler p = (CompoundTypeHandler) property; CompoundTypeHandler p = (CompoundTypeHandler) property;
@ -608,8 +610,10 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
private void ensureFree(int length) { private void ensureFree(int length) {
while (bufferPosition + length > data.capacity()) { while (bufferPosition + length > data.capacity()) {
byte[] bytes = this.data.array(); ByteBuffer bytes = this.data;
this.data = ByteBuffer.allocate(this.data.capacity() * 2); bytes.position(0);
BufferCache.release(this.data);
this.data = BufferCache.get((int) (bytes.capacity() * 1.5));
this.data.put(bytes); this.data.put(bytes);
} }
} }
@ -738,4 +742,9 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
} else return Varint.write(6); } else return Varint.write(6);
} }
} }
private String capitalize(String text) {
return text.substring(0, 1).toUpperCase() + text.substring(1);
}
} }

View file

@ -1,12 +1,12 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
/** /**
* Stores a double value. * Stores a double value.
*/ */
class DoubleHandler extends BuiltinTypeHandler<Double> { class DoubleHandler extends BuiltinTypeHandler<Double> {
public DoubleHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public DoubleHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(Double.class, propertyName, getter, setter); super(Double.class, propertyName, getter, setter);
} }

View file

@ -1,9 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
class FloatHandler extends BuiltinTypeHandler<Float> { class FloatHandler extends BuiltinTypeHandler<Float> {
public FloatHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public FloatHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(Float.class, propertyName, getter, setter); super(Float.class, propertyName, getter, setter);
} }

View file

@ -1,9 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
class IntegerHandler extends BuiltinTypeHandler<Integer> { class IntegerHandler extends BuiltinTypeHandler<Integer> {
public IntegerHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public IntegerHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(Integer.class, propertyName, getter, setter); super(Integer.class, propertyName, getter, setter);
} }

View file

@ -1,9 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
class LongHandler extends BuiltinTypeHandler<Long> { class LongHandler extends BuiltinTypeHandler<Long> {
public LongHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public LongHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(Long.class, propertyName, getter, setter); super(Long.class, propertyName, getter, setter);
} }

View file

@ -1,10 +1,10 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.util.*; import java.util.*;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
/** /**
* Base class that is a list of all the methods live that will likely not be implemented (pun intended) * Base class with all the methods that will not be implemented
* Only purpose: reduce linecount in the subclass. * Only purpose: reduce linecount in the subclass.
* *
* @param <E> * @param <E>

View file

@ -1,17 +1,19 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import com.github.shautvast.reflective.MetaMethod;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/* /*
* Maps the propertyvalue type to a PropertyHandler * Maps the propertyvalue type to a PropertyHandler
*/ */
final class PropertyHandlerFactory { final class PropertyHandlerFactory {
private static final Map<Class<?>, Class<? extends BuiltinTypeHandler<?>>> TYPE_HANDLERS = new HashMap<>(); private static final Map<Class<?>, Class<? extends BuiltinTypeHandler<?>>> BUILTIN = new HashMap<>();
private PropertyHandlerFactory() { private PropertyHandlerFactory() {
} }
@ -35,27 +37,23 @@ final class PropertyHandlerFactory {
register(BigDecimal.class, BigDecimalHandler.class); register(BigDecimal.class, BigDecimalHandler.class);
//Date/Timestamp //Date/Timestamp
//LocalDate/time //LocalDate/time
} }
public static boolean isBuiltInType(Class<?> type) { @SuppressWarnings("unchecked")
return TYPE_HANDLERS.containsKey(type); public static <T> Optional<TypeHandler> forType(Class<T> type, String name, MetaMethod getter, MetaMethod setter) {
} return Optional.ofNullable(BUILTIN.get(type)).map(appenderClass -> {
public static <T> BuiltinTypeHandler<T> forType(Class<T> type, String name, MethodHandle getter, MethodHandle setter) {
try { try {
Class<? extends BuiltinTypeHandler<?>> appenderClass = TYPE_HANDLERS.get(type); return (BuiltinTypeHandler<T>) appenderClass.getDeclaredConstructor(String.class, MetaMethod.class, MetaMethod.class)
if (appenderClass == null) {
throw new IllegalStateException("No Handler for " + type.getName());
}
return (BuiltinTypeHandler<T>) appenderClass.getDeclaredConstructor(String.class, MethodHandle.class, MethodHandle.class)
.newInstance(name, getter, setter); .newInstance(name, getter, setter);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) { InvocationTargetException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
});
} }
public static <T> BuiltinTypeHandler<T> forType(Class<T> type) { public static <T> Optional<TypeHandler> forType(Class<T> type) {
return forType(type, null, null, null); return forType(type, null, null, null);
} }
@ -63,6 +61,6 @@ final class PropertyHandlerFactory {
* register a new TypeHandler that cannot be derived from bean properties * register a new TypeHandler that cannot be derived from bean properties
*/ */
public static void register(Class<?> type, Class<? extends BuiltinTypeHandler<?>> typehandler) { public static void register(Class<?> type, Class<? extends BuiltinTypeHandler<?>> typehandler) {
TYPE_HANDLERS.put(type, typehandler); BUILTIN.put(type, typehandler);
} }
} }

View file

@ -1,9 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
class ShortHandler extends BuiltinTypeHandler<Short> { class ShortHandler extends BuiltinTypeHandler<Short> {
public ShortHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public ShortHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(Short.class, propertyName, getter, setter); super(Short.class, propertyName, getter, setter);
} }

View file

@ -1,9 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
class StringHandler extends BuiltinTypeHandler<String> { class StringHandler extends BuiltinTypeHandler<String> {
public StringHandler(String propertyName, MethodHandle getter, MethodHandle setter) { public StringHandler(String propertyName, MetaMethod getter, MetaMethod setter) {
super(String.class, propertyName, getter, setter); super(String.class, propertyName, getter, setter);
} }

View file

@ -1,9 +1,9 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.lang.invoke.MethodHandle; import com.github.shautvast.reflective.MetaMethod;
/** /**
* Abstract basertype over handlers for 'primitives' (ie. long, but also Long, * Abstract basetype over handlers for 'primitives' (ie. long, but also Long,
* String..=> built-in types) and compound types (your own). * String..=> built-in types) and compound types (your own).
* Common ancestor primarily to iterator over properties of any type. * Common ancestor primarily to iterator over properties of any type.
* The respective functions are completely different though, and we need `instanceof` to check for the * The respective functions are completely different though, and we need `instanceof` to check for the
@ -11,8 +11,8 @@ import java.lang.invoke.MethodHandle;
*/ */
public abstract class TypeHandler { public abstract class TypeHandler {
protected MethodHandle getter; // both can be null, if it's for a known ('primitive') type protected MetaMethod getter; // both can be null, if it's for a known ('primitive') type
protected MethodHandle setter; protected MetaMethod setter;
private final Class<?> type; private final Class<?> type;
/** /**
@ -20,26 +20,26 @@ public abstract class TypeHandler {
*/ */
private final String name; private final String name;
public TypeHandler(Class<?> type, String name, MethodHandle getter, MethodHandle setter) { public TypeHandler(Class<?> type, String name, MetaMethod getter, MetaMethod setter) {
this.type = type; this.type = type;
this.name = name; this.name = name;
this.getter = getter; this.getter = getter;
this.setter = setter; this.setter = setter;
} }
void setGetter(MethodHandle getter) { void setGetter(MetaMethod getter) {
this.getter = getter; this.getter = getter;
} }
public MethodHandle getGetter() { public MetaMethod getGetter() {
return getter; return getter;
} }
public MethodHandle getSetter() { public MetaMethod getSetter() {
return setter; return setter;
} }
void setSetter(MethodHandle setter) { void setSetter(MetaMethod setter) {
this.setter = setter; this.setter = setter;
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -8,7 +8,7 @@ import java.nio.charset.StandardCharsets;
* the layout is SQLite-like type:Varint, value byte[] * the layout is SQLite-like type:Varint, value byte[]
* Varint: byte[] * Varint: byte[]
*/ */
class ValueReader { public class ValueReader {
/** /**
* Reads a value from the buffer. * Reads a value from the buffer.

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -6,7 +6,7 @@ import java.nio.ByteBuffer;
* Writes integers to byte representation like Sqlite's putVarint64 * Writes integers to byte representation like Sqlite's putVarint64
* not threadsafe (take out B8 and B9 if you need that) * not threadsafe (take out B8 and B9 if you need that)
*/ */
final class Varint { public final class Varint {
private Varint() { private Varint() {
} }

View file

@ -0,0 +1,147 @@
package com.github.shautvast.contiguous.asm;
import com.github.shautvast.contiguous.ValueReader;
import com.github.shautvast.contiguous.Varint;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
public class AsmContiguousList<E> {
private static final byte[] DOUBLE_TYPE = {7};
private static final byte[] FLOAT_TYPE = {10}; // not in line with SQLite anymore
private static final int STRING_OFFSET = 13;
private static final int BYTES_OFFSET = 12; // blob TODO decide if include
public static final int MAX_24BITS = 8388607;
public static final long MAX_48BITS = 140737488355327L;
/*
* storage for dehydrated objects
*/
private ByteBuffer data = ByteBuffer.allocate(4096);//TODO create constructor with capacity
private int bufferPosition;
private final ArrayList<Integer> elementIndices = new ArrayList<>();
private int size;
private final AsmTypeHandler<?> rootHandler;
// private static final Map<Class<?>, TypeHandler> TYPE_HANDLERS = new HashMap<>();
public AsmContiguousList(Class<E> elementType) {
this.rootHandler = new StringHandler();
elementIndices.add(0); // index of first element
bufferPosition = 0;
}
public boolean add(E element) {
if (element == null) {
return false;
}
storePropertyData(element, rootHandler);
extend();
return true;
}
@SuppressWarnings("unchecked")
public E get(int index) {
checkbounds(index);
data.position(elementIndices.get(index));
Object read = ValueReader.read(data);
return (E) rootHandler.cast(read);
// create a new instance of the list element type
}
public String getAsJson(int index) {
checkbounds(index);
data.position(elementIndices.get(index));
if (rootHandler instanceof BuiltinHandler<?>) {
return getValue(rootHandler);
}
return null;
}
private void checkbounds(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index <0 or >" + size);
}
}
private String getValue(AsmTypeHandler<?> handler) {
String value = String.valueOf(ValueReader.read(data));
if (handler instanceof BuiltinHandler<?>) {
return quote(value);
}
return value;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
private void storePropertyData(Object element, AsmTypeHandler<?> typeHandler) {
if (typeHandler instanceof BuiltinHandler<?>) {
typeHandler.storePropertyValue(element);
}
}
private String quote(String out) {
out = "\"" + out + "\"";
return out;
}
private void store(byte[] bytes) {
ensureFree(bytes.length);
data.position(bufferPosition); // ensures intermittent reads/writes are safe
data.put(bytes);
bufferPosition += bytes.length;
}
private void store0() {
ensureFree(1);
data.put((byte) 0);
bufferPosition += 1;
}
void storeString(String value) {
if (value == null) {
store0();
} else {
byte[] utf = value.getBytes(StandardCharsets.UTF_8);
store(Varint.write(((long) (utf.length) << 1) + STRING_OFFSET));
store(utf);
}
}
private void ensureFree(int length) {
while (bufferPosition + length > data.capacity()) {
ByteBuffer bytes = this.data;
this.data = ByteBuffer.allocate(this.data.capacity() * 2);
this.data.put(bytes);
}
}
void extend() {
size += 1;
// keep track of index of element in data
elementIndices.add(bufferPosition);
}
class StringHandler extends BuiltinHandler<String> {
@Override
protected String getValue(Object instance) {
return instance.toString();
}
public void store(String value){
storeString(value);
}
}
}

View file

@ -0,0 +1,16 @@
package com.github.shautvast.contiguous.asm;
public abstract class AsmTypeHandler<T> {
public Object cast(Object value) {
return value;
}
void storePropertyValue(Object instance) {
T propertyValue = getValue(instance);
store(propertyValue);
}
protected abstract T getValue(Object instance);
public abstract void store(T value);
}

View file

@ -0,0 +1,4 @@
package com.github.shautvast.contiguous.asm;
public abstract class BuiltinHandler<T> extends AsmTypeHandler<T> {
}

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -7,6 +7,6 @@ import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
class ByteBean { public class ByteBean {
byte value; byte value;
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -74,7 +74,6 @@ public class ContiguousListTest {
assertArrayEquals(new byte[]{1, 42}, assertArrayEquals(new byte[]{1, 42},
beanList.getData()); beanList.getData());
assertArrayEquals(new int[]{0, 2}, beanList.getElementIndices());
} }
@Test @Test
@ -84,7 +83,6 @@ public class ContiguousListTest {
assertArrayEquals(new byte[]{1, -42}, assertArrayEquals(new byte[]{1, -42},
beanList.getData()); beanList.getData());
assertArrayEquals(new int[]{0, 2}, beanList.getElementIndices());
} }
@Test @Test
@ -206,7 +204,7 @@ public class ContiguousListTest {
@Test @Test
public void testSetterIterator() { void testSetterIterator() {
ContiguousList<NestedBean> integers = new ContiguousList<>(NestedBean.class); ContiguousList<NestedBean> integers = new ContiguousList<>(NestedBean.class);
ContiguousList<NestedBean>.SetterIterator iterator = integers.setterIterator(); ContiguousList<NestedBean>.SetterIterator iterator = integers.setterIterator();
@ -222,4 +220,17 @@ public class ContiguousListTest {
assertEquals("Magrathea", nestedBean.getStringBean().getName()); assertEquals("Magrathea", nestedBean.getStringBean().getName());
assertEquals(42, nestedBean.getIntBean().getValue()); assertEquals(42, nestedBean.getIntBean().getValue());
} }
@Test
void testLargeAllocations(){
ContiguousList<String> strings = new ContiguousList<>(String.class);
for (int i=0; i<1000; i++){
strings.add("...................");
}
ContiguousList<String> strings3 = new ContiguousList<>(String.class);
for (int i=0; i<1000; i++){
strings3.add("...................");
}
}
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
class DoubleBean { public class DoubleBean {
private Double value; private Double value;

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -7,6 +7,6 @@ import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
class FloatBean { public class FloatBean {
private Float value; private Float value;
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -7,6 +7,6 @@ import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
class IntBean { public class IntBean {
private int value; private int value;
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -7,6 +7,6 @@ import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
class LongBean { public class LongBean {
private long value; private long value;
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View file

@ -0,0 +1,11 @@
package com.github.shautvast.contiguous;
public class NestedBeanHandler extends CompoundTypeHandler{
NestedBeanHandler(Class<NestedBean> type) {
super(type);
}
NestedBeanHandler(Class<?> type, String propertyName) {
super(type, propertyName);
}
}

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
class ShortBean { public class ShortBean {
private short value; private short value;
} }

View file

@ -1,4 +1,4 @@
package nl.sanderhautvast.contiguous; package com.github.shautvast.contiguous;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View file

@ -0,0 +1,227 @@
package com.github.shautvast.contiguous.asm;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class AsmContiguousListTest {
@Test
public void testAddAndGetString() {
AsmContiguousList<String> list = new AsmContiguousList<>(String.class);
assertTrue(list.isEmpty());
list.add("hitchhikersguide to the galaxy");
assertFalse(list.isEmpty());
assertEquals(1, list.size());
list.add("the publishing houses of Ursa Minor");
assertFalse(list.isEmpty());
assertEquals(2, list.size());
String title = list.get(0);
assertEquals("hitchhikersguide to the galaxy", title);
String titleJson = list.getAsJson(0);
assertEquals("\"hitchhikersguide to the galaxy\"", titleJson);
String publisher = list.get(1);
assertEquals("the publishing houses of Ursa Minor", publisher);
}
// @Test
// public void testStringBean() {
// ContiguousList<StringBean> beanList = new ContiguousList<>(StringBean.class);
// beanList.add(new StringBean("Douglas Adams"));
//
// assertArrayEquals(new byte[]{39, 68, 111, 117, 103, 108, 97, 115, 32, 65, 100, 97, 109, 115}, beanList.getData());
//
// StringBean douglas = beanList.get(0);
// assertEquals("Douglas Adams", douglas.getName());
//
// String douglasJson = beanList.getAsJson(0);
// assertEquals("{\"name\": \"Douglas Adams\"}", douglasJson);
//
// // now add new data to see if existing data remains intact
// beanList.add(new StringBean("Ford Prefect"));
//
// assertEquals("Douglas Adams", beanList.get(0).getName());
// assertEquals("Ford Prefect", beanList.get(1).getName());
//
// assertEquals(2, beanList.size());
// }
//
// @Test
// public void testIntBean() {
// ContiguousList<IntBean> beanList = new ContiguousList<>(IntBean.class);
// beanList.add(new IntBean(42));
//
// assertArrayEquals(new byte[]{1, 42},
// beanList.getData());
// assertEquals(42, beanList.get(0).getValue());
// }
//
// @Test
// public void testLong() {
// ContiguousList<LongBean> beanList = new ContiguousList<>(LongBean.class);
// beanList.add(new LongBean(42));
//
// assertArrayEquals(new byte[]{1, 42},
// beanList.getData());
// assertEquals(42, beanList.get(0).getValue());
// }
//
// @Test
// public void testShort() {
// ContiguousList<ShortBean> beanList = new ContiguousList<>(ShortBean.class);
// beanList.add(new ShortBean((short) 42));
//
// assertArrayEquals(new byte[]{1, 42},
// beanList.getData());
// }
//
// @Test
// public void testByte() {
// ContiguousList<ByteBean> beanList = new ContiguousList<>(ByteBean.class);
// beanList.add(new ByteBean((byte) -42));
//
// assertArrayEquals(new byte[]{1, -42},
// beanList.getData());
// }
//
// @Test
// public void testNestedBean() {
// ContiguousList<NestedBean> beanList = new ContiguousList<>(NestedBean.class);
// beanList.add(new NestedBean(new StringBean("vogon constructor fleet"), new IntBean(42)));
//
// NestedBean expected = new NestedBean(new StringBean("vogon constructor fleet"), new IntBean(42));
// assertEquals(expected, beanList.get(0));
// }
//
// @Test
// public void testFloat() {
// ContiguousList<FloatBean> beanList = new ContiguousList<>(FloatBean.class);
// beanList.add(new FloatBean(1.1F));
//
//
// assertEquals(1.1F, beanList.get(0).getValue());
// }
//
// @Test
// public void testDouble() {
// ContiguousList<DoubleBean> beanList = new ContiguousList<>(DoubleBean.class);
// beanList.add(new DoubleBean(1.1));
//
// assertEquals(1.1, beanList.get(0).getValue());
// }
//
// @Test
// public void testNullFloat() {
// ContiguousList<FloatBean> beanList = new ContiguousList<>(FloatBean.class);
// beanList.add(new FloatBean(null));
//
// assertNull(beanList.get(0).getValue());
// }
//
// @Test
// public void testNullString() {
// ContiguousList<StringBean> beanList = new ContiguousList<>(StringBean.class);
// beanList.add(new StringBean(null));
//
// assertNull(beanList.get(0).getName());
// }
//
// @Test
// public void test100Elements() {
// ContiguousList<StringBean> beanList = new ContiguousList<>(StringBean.class);
// for (int i = 0; i < 100; i++) {
// beanList.add(new StringBean(null));
// }
// assertEquals(100, beanList.size());
// for (int i = 0; i < 100; i++) {
// assertNull(beanList.get(i).getName());
// }
// }
//
// @Test
// public void testBigInteger() {
// ContiguousList<BigInteger> bigIntegers = new ContiguousList<>(BigInteger.class);
// bigIntegers.add(new BigInteger("1000000000"));
// assertEquals(1_000_000_000L, bigIntegers.get(0).longValue());
// }
//
// @Test
// public void testIterator() {
// ContiguousList<Integer> integers = new ContiguousList<>(Integer.class);
// for (int i = 0; i < 100; i++) {
// integers.add(i);
// }
// int prevValue = -1;
// for (int value : integers) {
// assertEquals(prevValue, value - 1);
// prevValue = value;
// }
//
// integers.add(100);
//
// assertEquals(100, integers.get(100));
// }
//
// @Test
// public void testPrimitiveIterator() {
// ContiguousList<Integer> integers = new ContiguousList<>(Integer.class);
// for (int i = 0; i < 100; i++) {
// integers.add(i);
// }
// long prevValue = -1;
// for (Iterator<?> intIter = integers.valueIterator(); intIter.hasNext(); ) {
// int value = (int) intIter.next();
// assertEquals(prevValue, value - 1);
// prevValue = value;
// }
//
//
// integers.add(100);
//
// assertEquals(100, integers.get(100));
// }
//
// @Test
// public void testCompoundValueIterator() {
// ContiguousList<IntBean> integers = new ContiguousList<>(IntBean.class);
// for (int i = 0; i < 100; i++) {
// integers.add(new IntBean(i));
// }
// long prevValue = -1;
// for (Iterator<?> intIter = integers.valueIterator(); intIter.hasNext(); ) {
// int value = (int) intIter.next();
// assertEquals(prevValue, value - 1);
// prevValue = value;
// }
//
//
// integers.add(new IntBean(100));
//
// assertEquals(new IntBean(100), integers.get(100)); // here an instance
// }
//
//
//
// @Test
// public void testSetterIterator() {
// ContiguousList<NestedBean> integers = new ContiguousList<>(NestedBean.class);
// ContiguousList<NestedBean>.SetterIterator iterator = integers.setterIterator();
//
// if (iterator.hasNext()) {
// iterator.next().set("Magrathea");
// }
// if (iterator.hasNext()) {
// iterator.next().set(42);
// }
// iterator.finishObject();
//
// NestedBean nestedBean = integers.get(0);
// assertEquals("Magrathea", nestedBean.getStringBean().getName());
// assertEquals(42, nestedBean.getIntBean().getValue());
// }
}

View file

@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>nl.sanderhautvast</groupId> <groupId>com.github.shautvast</groupId>
<artifactId>contiguous-pom</artifactId> <artifactId>contiguous-pom</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
@ -13,6 +13,7 @@
<module>demo</module> <module>demo</module>
<module>jdbc</module> <module>jdbc</module>
<module>jackson</module> <module>jackson</module>
<module>benchmark</module>
</modules> </modules>
<name>contiguous</name> <name>contiguous</name>
<description>contiguous storage for java</description> <description>contiguous storage for java</description>