-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Таразанов Максим, ИТМО, М4139 #27
Changes from 10 commits
1bcbf8a
34d653d
23b8d91
2674b46
e44194f
0151fef
eccb8af
d24209b
4cabb0d
67c0d11
302882f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,8 @@ | |
build | ||
|
||
.idea | ||
.obsidian | ||
|
||
|
||
# Compiled source # | ||
################### | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package ru.vk.itmo.test.maximtarazanov; | ||
|
||
import ru.vk.itmo.Service; | ||
import ru.vk.itmo.ServiceConfig; | ||
import ru.vk.itmo.test.ServiceFactory; | ||
|
||
@ServiceFactory(stage = 1) | ||
public class MyFactory implements ServiceFactory.Factory { | ||
|
||
@Override | ||
public Service create(ServiceConfig config) { | ||
return new MyService(config); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package ru.vk.itmo.test.maximtarazanov; | ||
|
||
import one.nio.http.HttpServer; | ||
import one.nio.http.HttpServerConfig; | ||
import one.nio.http.HttpSession; | ||
import one.nio.http.Param; | ||
import one.nio.http.Path; | ||
import one.nio.http.Request; | ||
import one.nio.http.RequestMethod; | ||
import one.nio.http.Response; | ||
import one.nio.server.AcceptorConfig; | ||
import ru.vk.itmo.ServiceConfig; | ||
import ru.vk.itmo.dao.BaseEntry; | ||
import ru.vk.itmo.dao.Config; | ||
import ru.vk.itmo.dao.Entry; | ||
import ru.vk.itmo.test.maximtarazanov.dao.ReferenceDao; | ||
|
||
import java.io.IOException; | ||
import java.lang.foreign.MemorySegment; | ||
import java.lang.foreign.ValueLayout; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
public class MyServer extends HttpServer { | ||
|
||
private final ReferenceDao dao; | ||
private static final long FLUSH_THRESHOLD_BYTES = 1 << 20; | ||
private static final String PATH = "/v0/entity"; | ||
|
||
public MyServer(ServiceConfig config) throws IOException { | ||
super(createServerConfig(config)); | ||
dao = new ReferenceDao(new Config(config.workingDir(), FLUSH_THRESHOLD_BYTES)); | ||
} | ||
|
||
private static HttpServerConfig createServerConfig(ServiceConfig serviceConfig) { | ||
HttpServerConfig httpServerConfig = new HttpServerConfig(); | ||
AcceptorConfig acceptorConfig = new AcceptorConfig(); | ||
|
||
acceptorConfig.port = serviceConfig.selfPort(); | ||
acceptorConfig.reusePort = true; | ||
|
||
httpServerConfig.acceptors = new AcceptorConfig[]{acceptorConfig}; | ||
httpServerConfig.closeSessions = true; | ||
|
||
return httpServerConfig; | ||
} | ||
|
||
private static MemorySegment toMemorySegment(String string) { | ||
return MemorySegment.ofArray(string.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
public void close() { | ||
try { | ||
dao.close(); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@Path(PATH) | ||
@RequestMethod(Request.METHOD_GET) | ||
public final Response get(@Param(value = "id", required = true) String id) { | ||
MemorySegment key = | ||
(id == null || id.isEmpty()) | ||
? null | ||
: toMemorySegment(id); | ||
|
||
if (key == null) { | ||
return new Response(Response.BAD_REQUEST, Response.EMPTY); | ||
} | ||
|
||
Entry<MemorySegment> entry = dao.get(key); | ||
|
||
if (entry == null) { | ||
return new Response(Response.NOT_FOUND, Response.EMPTY); | ||
} | ||
|
||
return Response.ok(entry.value().toArray(ValueLayout.JAVA_BYTE)); | ||
} | ||
|
||
@Path(PATH) | ||
@RequestMethod(Request.METHOD_PUT) | ||
public final Response put(@Param(value = "id", required = true) String id, Request request) { | ||
MemorySegment key = | ||
(id == null || id.isEmpty()) | ||
? null | ||
: toMemorySegment(id); | ||
|
||
if (key == null) { | ||
return new Response(Response.BAD_REQUEST, Response.EMPTY); | ||
} | ||
|
||
Entry<MemorySegment> entry = new BaseEntry<>( | ||
key, | ||
MemorySegment.ofArray(request.getBody()) | ||
); | ||
|
||
dao.upsert(entry); | ||
|
||
return new Response(Response.CREATED, Response.EMPTY); | ||
} | ||
|
||
@Path(PATH) | ||
@RequestMethod(Request.METHOD_DELETE) | ||
public final Response delete(@Param(value = "id", required = true) String id) { | ||
MemorySegment key = | ||
(id == null || id.isEmpty()) | ||
? null | ||
: toMemorySegment(id); | ||
|
||
if (key == null) { | ||
return new Response(Response.BAD_REQUEST, Response.EMPTY); | ||
} | ||
|
||
dao.upsert(new BaseEntry<>(key, null)); | ||
|
||
return new Response(Response.ACCEPTED, Response.EMPTY); | ||
} | ||
|
||
@Path(PATH) | ||
public Response otherMethod() { | ||
return new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY); | ||
} | ||
|
||
@Override | ||
public void handleDefault(Request request, HttpSession session) throws IOException { | ||
Response response = new Response(Response.BAD_REQUEST, Response.EMPTY); | ||
session.sendResponse(response); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package ru.vk.itmo.test.maximtarazanov; | ||
|
||
import ru.vk.itmo.Service; | ||
import ru.vk.itmo.ServiceConfig; | ||
|
||
import java.io.IOException; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
public class MyService implements Service { | ||
private MyServer server; | ||
|
||
private ServiceConfig config; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Private field 'config' could be made final; it is only initialized in the declaration or constructor. |
||
|
||
public MyService(ServiceConfig config) { | ||
this.config = config; | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Void> start() throws IOException { | ||
server = new MyServer(config); | ||
server.start(); | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Void> stop() throws IOException { | ||
server.stop(); | ||
server.close(); | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ru.vk.itmo.test.maximtarazanov; | ||
|
||
import ru.vk.itmo.ServiceConfig; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.util.List; | ||
|
||
public class ServerMain { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All methods are static. Consider using a utility class instead. Alternatively, you could add a private constructor or make the class abstract to silence this warning. |
||
public static void main(String[] args) throws IOException { | ||
MyServer server = new MyServer( | ||
new ServiceConfig( | ||
8080, | ||
"http://localhost", | ||
List.of("http://localhost"), | ||
Files.createTempDirectory("dao") | ||
) | ||
); | ||
|
||
server.start(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package ru.vk.itmo.test.maximtarazanov.dao; | ||
|
||
import java.io.IOException; | ||
import java.lang.foreign.MemorySegment; | ||
import java.nio.ByteBuffer; | ||
|
||
/** | ||
* Growable buffer with {@link ByteBuffer} and {@link MemorySegment} interface. | ||
* | ||
* @author incubos | ||
*/ | ||
final class ByteArraySegment { | ||
private byte[] array; | ||
private MemorySegment segment; | ||
|
||
ByteArraySegment(final int capacity) { | ||
this.array = new byte[capacity]; | ||
this.segment = MemorySegment.ofArray(array); | ||
} | ||
|
||
void withArray(final ArrayConsumer consumer) throws IOException { | ||
consumer.process(array); | ||
} | ||
|
||
MemorySegment segment() { | ||
return segment; | ||
} | ||
|
||
void ensureCapacity(final long size) { | ||
if (size > Integer.MAX_VALUE) { | ||
throw new IllegalArgumentException("Too big!"); | ||
} | ||
|
||
final int capacity = (int) size; | ||
if (array.length >= capacity) { | ||
return; | ||
} | ||
|
||
// Grow to the nearest bigger power of 2 | ||
final int newSize = Integer.highestOneBit(capacity) << 1; | ||
array = new byte[newSize]; | ||
segment = MemorySegment.ofArray(array); | ||
} | ||
|
||
interface ArrayConsumer { | ||
void process(byte[] array) throws IOException; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package ru.vk.itmo.test.maximtarazanov.dao; | ||
|
||
import ru.vk.itmo.dao.Entry; | ||
|
||
import java.lang.foreign.MemorySegment; | ||
import java.util.Iterator; | ||
import java.util.NoSuchElementException; | ||
|
||
/** | ||
* Filters non tombstone {@link Entry}s. | ||
* | ||
* @author incubos | ||
*/ | ||
final class LiveFilteringIterator implements Iterator<Entry<MemorySegment>> { | ||
private final Iterator<Entry<MemorySegment>> delegate; | ||
private Entry<MemorySegment> next; | ||
|
||
LiveFilteringIterator(final Iterator<Entry<MemorySegment>> delegate) { | ||
this.delegate = delegate; | ||
skipTombstones(); | ||
} | ||
|
||
private void skipTombstones() { | ||
while (delegate.hasNext()) { | ||
final Entry<MemorySegment> entry = delegate.next(); | ||
if (entry.value() != null) { | ||
this.next = entry; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public boolean hasNext() { | ||
return next != null; | ||
} | ||
|
||
@Override | ||
public Entry<MemorySegment> next() { | ||
if (!hasNext()) { | ||
throw new NoSuchElementException(); | ||
} | ||
|
||
// Consume | ||
final Entry<MemorySegment> result = next; | ||
next = null; | ||
|
||
skipTombstones(); | ||
|
||
return result; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package ru.vk.itmo.test.maximtarazanov.dao; | ||
|
||
import ru.vk.itmo.dao.Entry; | ||
|
||
import java.lang.foreign.MemorySegment; | ||
import java.util.Iterator; | ||
import java.util.NavigableMap; | ||
import java.util.concurrent.ConcurrentSkipListMap; | ||
|
||
/** | ||
* Memory table. | ||
* | ||
* @author incubos | ||
*/ | ||
final class MemTable { | ||
private final NavigableMap<MemorySegment, Entry<MemorySegment>> map = | ||
new ConcurrentSkipListMap<>( | ||
MemorySegmentComparator.INSTANCE); | ||
|
||
boolean isEmpty() { | ||
return map.isEmpty(); | ||
} | ||
|
||
Iterator<Entry<MemorySegment>> get( | ||
final MemorySegment from, | ||
final MemorySegment to) { | ||
if (from == null && to == null) { | ||
// All | ||
return map.values().iterator(); | ||
} else if (from == null) { | ||
// Head | ||
return map.headMap(to).values().iterator(); | ||
} else if (to == null) { | ||
// Tail | ||
return map.tailMap(from).values().iterator(); | ||
} else { | ||
// Slice | ||
return map.subMap(from, to).values().iterator(); | ||
} | ||
} | ||
|
||
Entry<MemorySegment> get(final MemorySegment key) { | ||
return map.get(key); | ||
} | ||
|
||
Entry<MemorySegment> upsert(final Entry<MemorySegment> entry) { | ||
return map.put(entry.key(), entry); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Define and throw a dedicated exception instead of using a generic one.