Removed libraries.

This commit is contained in:
Fabian Schlenz 2016-06-29 11:00:29 +02:00
parent 2ccdff9d87
commit 3f5fd7787c
129 changed files with 0 additions and 12296 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,297 +0,0 @@
package com.droidkit.actors;
import com.droidkit.actors.mailbox.Mailbox;
import com.droidkit.actors.messages.DeadLetter;
import com.droidkit.actors.messages.NamedMessage;
import com.droidkit.actors.tasks.*;
import com.droidkit.actors.tasks.messages.TaskError;
import com.droidkit.actors.tasks.messages.TaskResult;
import com.droidkit.actors.tasks.messages.TaskTimeout;
import java.lang.reflect.Array;
import java.util.UUID;
/**
* Actor object
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class Actor {
private UUID uuid;
private String path;
private ActorContext context;
private Mailbox mailbox;
private ActorAskImpl askPattern;
public Actor() {
}
/**
* <p>INTERNAL API</p>
* Initialization of actor
*
* @param uuid uuid of actor
* @param path path of actor
* @param context context of actor
* @param mailbox mailbox of actor
*/
public final void initActor(UUID uuid, String path, ActorContext context, Mailbox mailbox) {
this.uuid = uuid;
this.path = path;
this.context = context;
this.mailbox = mailbox;
this.askPattern = new ActorAskImpl(self());
}
/**
* Actor System
*
* @return Actor System
*/
public final ActorSystem system() {
return context.getSystem();
}
/**
* Self actor reference
*
* @return self reference
*/
public final ActorRef self() {
return context.getSelf();
}
/**
* Actor context
*
* @return context
*/
protected final ActorContext context() {
return context;
}
/**
* Sender of last received message
*
* @return sender's ActorRef
*/
public final ActorRef sender() {
return context.sender();
}
/**
* Actor UUID
*
* @return uuid
*/
protected final UUID getUuid() {
return uuid;
}
/**
* Actor path
*
* @return path
*/
protected final String getPath() {
return path;
}
/**
* Actor mailbox
*
* @return mailbox
*/
public final Mailbox getMailbox() {
return mailbox;
}
/**
* Called before first message receiving
*/
public void preStart() {
}
public final void onReceiveGlobal(Object message) {
if (message instanceof DeadLetter) {
if (askPattern.onDeadLetter((DeadLetter) message)) {
return;
}
} else if (message instanceof TaskResult) {
if (askPattern.onTaskResult((TaskResult) message)) {
return;
}
} else if (message instanceof TaskTimeout) {
if (askPattern.onTaskTimeout((TaskTimeout) message)) {
return;
}
} else if (message instanceof TaskError) {
if (askPattern.onTaskError((TaskError) message)) {
return;
}
}
onReceive(message);
}
/**
* Receiving of message
*
* @param message message
*/
public void onReceive(Object message) {
}
/**
* Called after actor shutdown
*/
public void postStop() {
}
/**
* Reply message to sender of last message
*
* @param message reply message
*/
public void reply(Object message) {
if (context.sender() != null) {
context.sender().send(message, self());
}
}
public AskFuture combine(AskFuture... futures) {
return askPattern.combine(futures);
}
public AskFuture combine(AskCallback<Object[]> callback, AskFuture... futures) {
AskFuture future = combine(futures);
future.addListener(callback);
return future;
}
public <T> AskFuture combine(final String name, final Class<T> clazz, AskFuture... futures) {
return combine(new AskCallback<Object[]>() {
@Override
public void onResult(Object[] result) {
T[] res = (T[]) Array.newInstance(clazz, result.length);
for (int i = 0; i < result.length; i++) {
res[i] = (T) result[i];
}
self().send(new NamedMessage(name, res));
}
@Override
public void onError(Throwable throwable) {
self().send(new NamedMessage(name, throwable));
}
}, futures);
}
public <T> AskFuture combine(final String name, AskFuture... futures) {
return combine(new AskCallback<Object[]>() {
@Override
public void onResult(Object[] result) {
self().send(new NamedMessage(name, result));
}
@Override
public void onError(Throwable throwable) {
self().send(new NamedMessage(name, throwable));
}
}, futures);
}
/**
* Ask TaskActor for result
*
* @param selection ActorSelection of task
* @return Future
*/
public AskFuture ask(ActorSelection selection) {
return askPattern.ask(system().actorOf(selection), 0, null);
}
/**
* Ask TaskActor for result
*
* @param selection ActorSelection of task
* @param timeout timeout of task
* @return Future
*/
public AskFuture ask(ActorSelection selection, long timeout) {
return askPattern.ask(system().actorOf(selection), timeout, null);
}
/**
* Ask TaskActor for result
*
* @param selection ActorSelection of task
* @param callback callback for ask
* @return Future
*/
public AskFuture ask(ActorSelection selection, AskCallback callback) {
return askPattern.ask(system().actorOf(selection), 0, callback);
}
/**
* Ask TaskActor for result
*
* @param selection ActorSelection of task
* @param timeout timeout of task
* @param callback callback for ask
* @return Future
*/
public AskFuture ask(ActorSelection selection, long timeout, AskCallback callback) {
return askPattern.ask(system().actorOf(selection), timeout, callback);
}
/**
* Ask TaskActor for result
*
* @param ref ActorRef of task
* @return Future
*/
public AskFuture ask(ActorRef ref) {
return askPattern.ask(ref, 0, null);
}
/**
* Ask TaskActor for result
*
* @param ref ActorRef of task
* @param timeout timeout of task
* @return Future
*/
public AskFuture ask(ActorRef ref, long timeout) {
return askPattern.ask(ref, timeout, null);
}
/**
* Ask TaskActor for result
*
* @param ref ActorRef of task
* @param callback callback for ask
* @return Future
*/
public AskFuture ask(ActorRef ref, AskCallback callback) {
return askPattern.ask(ref, 0, callback);
}
/**
* Ask TaskActor for result
*
* @param ref ActorRef of task
* @param timeout timeout of task
* @param callback callback for ask
* @return Future
*/
public AskFuture ask(ActorRef ref, long timeout, AskCallback callback) {
return askPattern.ask(ref, timeout, callback);
}
}

View File

@ -1,59 +0,0 @@
package com.droidkit.actors;
/**
* Context of actor
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ActorContext {
private final ActorScope actorScope;
/**
* <p>INTERNAL API</p>
* Creating of actor context
*
* @param scope actor scope
*/
public ActorContext(ActorScope scope) {
this.actorScope = scope;
}
/**
* Actor Reference
*
* @return reference
*/
public ActorRef getSelf() {
return actorScope.getActorRef();
}
/**
* Actor system
*
* @return Actor system
*/
public ActorSystem getSystem() {
return actorScope.getActorSystem();
}
/**
* Sender of last received message
*
* @return sender's ActorRef
*/
public ActorRef sender() {
return actorScope.getSender();
}
/**
* Stopping actor
*/
public void stopSelf() {
try {
actorScope.shutdownActor();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -1,15 +0,0 @@
package com.droidkit.actors;
/**
* Object for manual actors creating
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public interface ActorCreator<T extends Actor> {
/**
* Create actor
*
* @return Actor
*/
public T create();
}

View File

@ -1,121 +0,0 @@
package com.droidkit.actors;
import com.droidkit.actors.mailbox.AbsActorDispatcher;
import java.util.UUID;
/**
* Reference to Actor that allows to send messages to real Actor
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ActorRef {
private ActorSystem system;
private AbsActorDispatcher dispatcher;
private UUID uuid;
private String path;
public UUID getUuid() {
return uuid;
}
public String getPath() {
return path;
}
/**
* <p>INTERNAL API</p>
* Creating actor reference
*
* @param system actor system
* @param dispatcher dispatcher of actor
* @param path path of actor
* @param uuid uuid of actor
*/
public ActorRef(ActorSystem system, AbsActorDispatcher dispatcher, UUID uuid, String path) {
this.system = system;
this.dispatcher = dispatcher;
this.uuid = uuid;
this.path = path;
}
/**
* Send message with empty sender
*
* @param message message
*/
public void send(Object message) {
send(message, null);
}
/**
* Send message with specified sender
*
* @param message message
* @param sender sender
*/
public void send(Object message, ActorRef sender) {
send(message, 0, sender);
}
/**
* Send message with empty sender and delay
*
* @param message message
* @param delay delay
*/
public void send(Object message, long delay) {
send(message, delay, null);
}
/**
* Send message
*
* @param message message
* @param delay delay
* @param sender sender
*/
public void send(Object message, long delay, ActorRef sender) {
dispatcher.sendMessage(path, message, ActorTime.currentTime() + delay, sender);
}
/**
* Send message once
*
* @param message message
*/
public void sendOnce(Object message) {
send(message, null);
}
/**
* Send message once
*
* @param message message
* @param sender sender
*/
public void sendOnce(Object message, ActorRef sender) {
sendOnce(message, 0, sender);
}
/**
* Send message once
*
* @param message message
* @param delay delay
*/
public void sendOnce(Object message, long delay) {
sendOnce(message, delay, null);
}
/**
* Send message once
*
* @param message message
* @param delay delay
* @param sender sender
*/
public void sendOnce(Object message, long delay, ActorRef sender) {
dispatcher.sendMessageOnce(path, message, ActorTime.currentTime() + delay, sender);
}
}

View File

@ -1,128 +0,0 @@
package com.droidkit.actors;
import com.droidkit.actors.mailbox.AbsActorDispatcher;
import com.droidkit.actors.mailbox.Mailbox;
import java.util.UUID;
/**
* <p>INTERNAL API</p>
* Actor Scope contains states of actor, UUID, Path, Props and Actor (if created).
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ActorScope {
public static final int STATE_STARTING = 0;
public static final int STATE_RUNNING = 1;
public static final int STATE_SHUTDOWN = 2;
private final UUID uuid;
private final String path;
private final Props props;
private final ActorRef actorRef;
private final Mailbox mailbox;
private final AbsActorDispatcher dispatcher;
private final ActorSystem actorSystem;
private int state;
private Actor actor;
private ActorRef sender;
public ActorScope(ActorSystem actorSystem, Mailbox mailbox, ActorRef actorRef, AbsActorDispatcher dispatcher, UUID uuid, String path, Props props) {
this.actorSystem = actorSystem;
this.mailbox = mailbox;
this.actorRef = actorRef;
this.dispatcher = dispatcher;
this.uuid = uuid;
this.path = path;
this.props = props;
this.state = STATE_STARTING;
}
public AbsActorDispatcher getDispatcher() {
return dispatcher;
}
public int getState() {
return state;
}
public UUID getUuid() {
return uuid;
}
public String getPath() {
return path;
}
public Props getProps() {
return props;
}
public ActorRef getActorRef() {
return actorRef;
}
public Mailbox getMailbox() {
return mailbox;
}
public Actor getActor() {
return actor;
}
public ActorSystem getActorSystem() {
return actorSystem;
}
public ActorRef getSender() {
return sender;
}
public void setSender(ActorRef sender) {
this.sender = sender;
}
/**
* Create actor
*
* @throws Exception
*/
public void createActor() throws Exception {
if (state == STATE_STARTING) {
actor = props.create();
CurrentActor.setCurrentActor(actor);
actor.initActor(getUuid(), getPath(), new ActorContext(this), getMailbox());
actor.preStart();
} else if (state == STATE_RUNNING) {
throw new RuntimeException("Actor already created");
} else if (state == STATE_SHUTDOWN) {
throw new RuntimeException("Actor shutdown");
} else {
throw new RuntimeException("Unknown ActorScope state");
}
}
/**
* Shutdown actor
*
* @throws Exception
*/
public void shutdownActor() throws Exception {
if (state == STATE_STARTING || state == STATE_RUNNING ||
state == STATE_SHUTDOWN) {
actorSystem.removeActor(this);
dispatcher.disconnectScope(this);
actor.postStop();
actor = null;
} else {
throw new RuntimeException("Unknown ActorScope state");
}
}
}

View File

@ -1,24 +0,0 @@
package com.droidkit.actors;
/**
* Actor selection: group and path of actor
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ActorSelection {
private final Props props;
private final String path;
public ActorSelection(Props props, String path) {
this.props = props;
this.path = path;
}
public Props getProps() {
return props;
}
public String getPath() {
return path;
}
}

View File

@ -1,129 +0,0 @@
package com.droidkit.actors;
import com.droidkit.actors.mailbox.AbsActorDispatcher;
import com.droidkit.actors.mailbox.ActorDispatcher;
import java.util.HashMap;
/**
* Entry point for Actor Model, creates all actors and dispatchers
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ActorSystem {
private static final ActorSystem mainSystem = new ActorSystem();
/**
* Main actor system
*
* @return ActorSystem
*/
public static ActorSystem system() {
return mainSystem;
}
private static final String DEFAULT_DISPATCHER = "default";
private final HashMap<String, AbsActorDispatcher> dispatchers = new HashMap<String, AbsActorDispatcher>();
private final HashMap<String, ActorScope> actors = new HashMap<String, ActorScope>();
/**
* Creating new actor system
*/
public ActorSystem() {
addDispatcher(DEFAULT_DISPATCHER);
}
/**
* Adding dispatcher with threads count = {@code Runtime.getRuntime().availableProcessors()}
*
* @param dispatcherId dispatcher id
*/
public void addDispatcher(String dispatcherId) {
addDispatcher(dispatcherId, new ActorDispatcher(this, Runtime.getRuntime().availableProcessors()));
}
/**
* Registering custom dispatcher
*
* @param dispatcherId dispatcher id
* @param dispatcher dispatcher object
*/
public void addDispatcher(String dispatcherId, AbsActorDispatcher dispatcher) {
synchronized (dispatchers) {
if (dispatchers.containsKey(dispatcherId)) {
return;
}
dispatchers.put(dispatcherId, dispatcher);
}
}
public <T extends Actor> ActorRef actorOf(ActorSelection selection) {
return actorOf(selection.getProps(), selection.getPath());
}
/**
* Creating or getting existing actor from actor class
*
* @param actor Actor Class
* @param path Actor Path
* @param <T> Actor Class
* @return ActorRef
*/
public <T extends Actor> ActorRef actorOf(Class<T> actor, String path) {
return actorOf(Props.create(actor), path);
}
/**
* Creating or getting existing actor from actor props
*
* @param props Actor Props
* @param path Actor Path
* @return ActorRef
*/
public ActorRef actorOf(Props props, String path) {
// TODO: Remove lock
synchronized (actors) {
// Searching for already created actor
ActorScope scope = actors.get(path);
// If already created - return ActorRef
if (scope != null) {
return scope.getActorRef();
}
// Finding dispatcher for actor
String dispatcherId = props.getDispatcher() == null ? DEFAULT_DISPATCHER : props.getDispatcher();
AbsActorDispatcher mailboxesDispatcher;
synchronized (dispatchers) {
if (!dispatchers.containsKey(dispatcherId)) {
throw new RuntimeException("Unknown dispatcherId '" + dispatcherId + "'");
}
mailboxesDispatcher = dispatchers.get(dispatcherId);
}
// Creating actor scope
scope = mailboxesDispatcher.createScope(path, props);
// Saving actor in collection
actors.put(path, scope);
return scope.getActorRef();
}
}
/**
* WARRING! Call only during processing message in actor!
*
* @param scope Actor Scope
*/
void removeActor(ActorScope scope) {
synchronized (actors) {
if (actors.get(scope.getPath()) == scope) {
actors.remove(scope.getPath());
}
}
}
}

View File

@ -1,27 +0,0 @@
package com.droidkit.actors;
/**
* Time used by actor system, uses System.nanoTime() inside
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ActorTime {
private static long lastTime = 0;
private static final Object timeLock = new Object();
/**
* Getting current actor system time
*
* @return actor system time
*/
public static long currentTime() {
long res = System.nanoTime() / 1000000;
synchronized (timeLock) {
if (lastTime < res) {
lastTime = res;
}
return lastTime;
}
}
}

View File

@ -1,19 +0,0 @@
package com.droidkit.actors;
/**
* <p>INTERNAL API!</p>
* Keeps current actor for thread. Will be used for better implementations of patterns.
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class CurrentActor {
private static ThreadLocal<Actor> currentActor = new ThreadLocal<Actor>();
public static void setCurrentActor(Actor actor) {
currentActor.set(actor);
}
public static Actor getCurrentActor() {
return currentActor.get();
}
}

View File

@ -1,88 +0,0 @@
package com.droidkit.actors;
/**
* <p>Props is a configuration class to specify options for the creation of actors, think of it as an immutable and
* thus freely shareable recipe for creating an actor including associated dispatcher information.</p>
* For more information you may read about <a href="http://doc.akka.io/docs/akka/2.3.5/java/untyped-actors.html">Akka Props</a>.
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public final class Props<T extends Actor> {
private static final int TYPE_DEFAULT = 1;
private static final int TYPE_CREATOR = 2;
private final Class<T> aClass;
private final Object[] args;
private final int type;
private final ActorCreator<T> creator;
private final String dispatcher;
private Props(Class<T> aClass, Object[] args, int type, String dispatcher, ActorCreator<T> creator) {
this.aClass = aClass;
this.args = args;
this.type = type;
this.creator = creator;
this.dispatcher = dispatcher;
}
/**
* Creating actor from Props
*
* @return Actor
* @throws Exception
*/
public T create() throws Exception {
if (type == TYPE_DEFAULT) {
if (args == null || args.length == 0) {
return aClass.newInstance();
}
} else if (type == TYPE_CREATOR) {
return creator.create();
}
throw new RuntimeException("Unsupported create method");
}
/**
* Getting dispatcher id if available
*
* @return
*/
public String getDispatcher() {
return dispatcher;
}
/**
* Changing dispatcher
*
* @param dispatcher dispatcher id
* @return this
*/
public Props<T> changeDispatcher(String dispatcher) {
return new Props<T>(aClass, args, type, dispatcher, creator);
}
/**
* Create props from class
*
* @param tClass Actor class
* @param <T> Actor class
* @return Props object
*/
public static <T extends Actor> Props<T> create(Class<T> tClass) {
return new Props(tClass, null, TYPE_DEFAULT, null, null);
}
/**
* Create props from Actor creator
*
* @param clazz Actor class
* @param creator Actor creator class
* @param <T> Actor class
* @return Props object
*/
public static <T extends Actor> Props<T> create(Class<T> clazz, ActorCreator<T> creator) {
return new Props<T>(clazz, null, TYPE_CREATOR, null, creator);
}
}

View File

@ -1,137 +0,0 @@
package com.droidkit.actors;
import com.droidkit.actors.messages.NamedMessage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
/**
* ReflectedActor is Actor that uses java reflection for processing of messages
* For each message developer must create method named "onReceive" with one argument
* with type of message
* For special message {@link com.droidkit.actors.messages.NamedMessage} you can create method
* named like {@code onDownloadReceive}. First letter in {@code Download} will be lowed and ReflectedActor
* will use this as {@code download} for name of message.
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ReflectedActor extends Actor {
private ArrayList<Event> events = new ArrayList<Event>();
private ArrayList<NamedEvent> namedEvents = new ArrayList<NamedEvent>();
@Override
public final void preStart() {
Method[] methods = getClass().getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("onReceive") && m.getParameterTypes().length == 1) {
if (m.getName().equals("onReceive") && m.getParameterTypes()[0] == Object.class) {
continue;
}
events.add(new Event(m.getParameterTypes()[0], m));
continue;
}
if (m.getName().startsWith("on") && m.getName().endsWith("Receive")) {
String methodName = m.getName();
String name = methodName.substring("on".length(), methodName.length() - "Receive".length());
if (name.length() > 0) {
name = name.substring(0, 1).toLowerCase() + name.substring(1);
namedEvents.add(new NamedEvent(name, m.getParameterTypes()[0], m));
continue;
}
}
}
preStartImpl();
}
/**
* Replacement for preStart
*/
public void preStartImpl() {
}
@Override
public void onReceive(Object message) {
if (message instanceof NamedMessage) {
NamedMessage named = (NamedMessage) message;
for (NamedEvent event : namedEvents) {
if (event.name.equals(named.getName())) {
if (event.check(named.getMessage())) {
try {
event.method.invoke(this, named.getMessage());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
for (Event event : events) {
if (event.check(message)) {
try {
event.method.invoke(this, message);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return;
}
}
}
class NamedEvent {
private String name;
private Class arg;
private Method method;
NamedEvent(String name, Class arg, Method method) {
this.name = name;
this.arg = arg;
this.method = method;
}
public String getName() {
return name;
}
public Class getArg() {
return arg;
}
public Method getMethod() {
return method;
}
public boolean check(Object obj) {
if (arg.isAssignableFrom(obj.getClass())) {
return true;
}
return false;
}
}
class Event {
private Class arg;
private Method method;
Event(Class arg, Method method) {
this.arg = arg;
this.method = method;
}
public boolean check(Object obj) {
if (arg.isAssignableFrom(obj.getClass())) {
return true;
}
return false;
}
}
}

View File

@ -1,84 +0,0 @@
package com.droidkit.actors.dispatch;
/**
* Queue for dispatching messages for {@link ThreadPoolDispatcher}.
* Implementation MUST BE thread-safe.
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public abstract class AbstractDispatchQueue<T> {
/**
* Value used for result of waitDelay when dispatcher need to wait forever
*/
protected static final long FOREVER = Long.MAX_VALUE;
private QueueListener listener;
/**
* Fetch message for dispatching and removing it from dispatch queue
*
* @param time current time from ActorTime
* @return message or null if there is no message for processing
*/
public abstract T dispatch(long time);
/**
* Expected delay for nearest message.
* You might provide most accurate value as you can,
* this will minimize unnecessary thread work.
* For example, if you will return zero here then thread will
* loop continuously and consume processor time.
*
* @param time current time from ActorTime
* @return delay in ms
*/
public abstract long waitDelay(long time);
/**
* Implementation of adding message to queue
*
* @param message
* @param atTime
*/
protected abstract void putToQueueImpl(T message, long atTime);
/**
* Adding message to queue
*
* @param message message
* @param atTime time (use {@link com.droidkit.actors.ActorTime#currentTime()} for currentTime)
*/
public final void putToQueue(T message, long atTime) {
putToQueueImpl(message, atTime);
notifyQueueChanged();
}
/**
* Notification about queue change.
*/
protected void notifyQueueChanged() {
QueueListener lListener = listener;
if (lListener != null) {
lListener.onQueueChanged();
}
}
/**
* Getting of current queue listener
*
* @return queue listener
*/
public QueueListener getListener() {
return listener;
}
/**
* Setting queue listener
*
* @param listener queue listener
*/
public void setListener(QueueListener listener) {
this.listener = listener;
}
}

View File

@ -1,47 +0,0 @@
package com.droidkit.actors.dispatch;
/**
* Abstract thread dispatcher for messages
*/
public abstract class AbstractDispatcher<T, Q extends AbstractDispatchQueue<T>> {
final private Q queue;
final Dispatch<T> dispatch;
protected AbstractDispatcher(Q queue, Dispatch<T> dispatch) {
this.queue = queue;
this.dispatch = dispatch;
this.queue.setListener(new QueueListener() {
@Override
public void onQueueChanged() {
notifyDispatcher();
}
});
}
/**
* Queue used for dispatching
*
* @return queue
*/
public Q getQueue() {
return queue;
}
/**
* Actual execution of action
*
* @param message action
*/
protected void dispatchMessage(T message) {
if (dispatch != null) {
dispatch.dispatchMessage(message);
}
}
/**
* Notification about queue change
*/
protected void notifyDispatcher() {
}
}

View File

@ -1,8 +0,0 @@
package com.droidkit.actors.dispatch;
/**
* Used as callback for message processing
*/
public interface Dispatch<T> {
void dispatchMessage(T message);
}

View File

@ -1,10 +0,0 @@
package com.droidkit.actors.dispatch;
/**
* Listener for monitoring queue changes in dispatchers
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public interface QueueListener {
public void onQueueChanged();
}

View File

@ -1,70 +0,0 @@
package com.droidkit.actors.dispatch;
import static com.droidkit.actors.ActorTime.currentTime;
/**
* RunnableDispatcher is used for executing various Runnable in background
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class RunnableDispatcher extends ThreadPoolDispatcher<Runnable, SimpleDispatchQueue<Runnable>> {
/**
* Creating of dispatcher with one thread
*/
public RunnableDispatcher() {
this(1);
}
/**
* Creating of dispatcher with {@code threadsCount} threads
*
* @param threadsCount number of threads
*/
public RunnableDispatcher(int threadsCount) {
super(threadsCount, new SimpleDispatchQueue<Runnable>());
}
/**
* Creating of dispatcher with {@code threadsCount} threads and {@code priority}
*
* @param threadsCount number of threads
* @param priority priority of threads
*/
public RunnableDispatcher(int threadsCount, int priority) {
super(threadsCount, priority, new SimpleDispatchQueue<Runnable>());
}
@Override
protected void dispatchMessage(Runnable object) {
object.run();
}
/**
* Post action to queue
*
* @param action action
*/
public void postAction(Runnable action) {
postAction(action, 0);
}
/**
* Post action to queue with delay
*
* @param action action
* @param delay delay
*/
public void postAction(Runnable action, long delay) {
getQueue().putToQueue(action, currentTime() + delay);
}
/**
* Removing action from queue
*
* @param action action
*/
public void removeAction(Runnable action) {
getQueue().removeFromQueue(action);
}
}

View File

@ -1,116 +0,0 @@
package com.droidkit.actors.dispatch;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
/**
* Simple queue implementation for dispatchers
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class SimpleDispatchQueue<T> extends AbstractDispatchQueue<T> {
protected final TreeMap<Long, Message> messages = new TreeMap<Long, Message>();
protected final ArrayList<Message> freeMessages = new ArrayList<Message>();
/**
* Removing message from queue
*
* @param t message
*/
public void removeFromQueue(T t) {
synchronized (messages) {
for (Map.Entry<Long, Message> messageEntry : messages.entrySet()) {
if (messageEntry.getValue().equals(t)) {
Message message = messages.remove(messageEntry.getKey());
recycle(message);
notifyQueueChanged();
return;
}
}
}
}
@Override
public T dispatch(long time) {
synchronized (messages) {
if (messages.size() > 0) {
long firstKey = messages.firstKey();
if (firstKey < time) {
Message message = messages.remove(firstKey);
T res = message.action;
recycle(message);
return res;
}
}
}
return null;
}
@Override
public long waitDelay(long time) {
synchronized (messages) {
if (messages.size() > 0) {
long firstKey = messages.firstKey();
if (firstKey < time) {
return 0;
} else {
return time - firstKey;
}
}
}
return FOREVER;
}
@Override
public void putToQueueImpl(T action, long atTime) {
Message message = obtainMessage();
message.setMessage(action, atTime);
synchronized (messages) {
while (messages.containsKey(atTime)) {
atTime++;
}
messages.put(atTime, message);
}
}
/**
* Getting new message object for writing to queue
*
* @return Message object
*/
protected Message obtainMessage() {
synchronized (freeMessages) {
if (freeMessages.size() > 0) {
return freeMessages.remove(0);
}
}
return new Message();
}
/**
* Saving message object to free cache
*
* @param message Message object
*/
protected void recycle(Message message) {
synchronized (freeMessages) {
freeMessages.add(message);
}
}
/**
* Holder for messages
*/
protected class Message {
public long destTime;
public T action;
public void setMessage(T action, long destTime) {
this.action = action;
this.destTime = destTime;
}
}
}

View File

@ -1,129 +0,0 @@
package com.droidkit.actors.dispatch;
import static com.droidkit.actors.ActorTime.currentTime;
/**
* ThreadPoolDispatcher is used for dispatching messages on it's own threads.
* Class is completely thread-safe.
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ThreadPoolDispatcher<T, Q extends AbstractDispatchQueue<T>> extends AbstractDispatcher<T, Q> {
private final Thread[] threads;
private boolean isClosed = false;
/**
* Dispatcher constructor. Create threads with NORM_PRIORITY.
*
* @param count thread count
* @param queue queue for messages
* (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information)
* @param dispatch Dispatch for message processing
*/
public ThreadPoolDispatcher(int count, Q queue, Dispatch<T> dispatch) {
this(count, Thread.NORM_PRIORITY, queue, dispatch);
}
/**
* Dispatcher constructor. Create threads with NORM_PRIORITY.
* Should override dispatchMessage for message processing.
*
* @param count thread count
* @param queue queue for messages
* (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information)
*/
public ThreadPoolDispatcher(int count, Q queue) {
this(count, Thread.NORM_PRIORITY, queue, null);
}
/**
* Dispatcher constructor. Create threads with NORM_PRIORITY.
* Should override dispatchMessage for message processing.
*
* @param count thread count
* @param priority thread priority
* @param queue queue for messages
* (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information)
*/
public ThreadPoolDispatcher(int count, int priority, Q queue) {
this(count, priority, queue, null);
}
/**
* Dispatcher constructor
*
* @param count thread count
* @param priority thread priority
* @param queue queue for messages
* (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information)
* @param dispatch Dispatch for message processing
*/
public ThreadPoolDispatcher(int count, int priority, final Q queue, Dispatch<T> dispatch) {
super(queue, dispatch);
this.threads = new Thread[count];
for (int i = 0; i < count; i++) {
this.threads[i] = new DispatcherThread();
this.threads[i].setPriority(priority);
this.threads[i].start();
}
}
/**
* Closing of dispatcher no one actions will be executed after calling this method.
*/
public void close() {
isClosed = true;
notifyDispatcher();
}
/**
* Notification about queue change
*/
@Override
protected void notifyDispatcher() {
if (threads != null) {
synchronized (threads) {
threads.notifyAll();
}
}
}
/**
* Thread class for dispatching
*/
private class DispatcherThread extends Thread {
@Override
public void run() {
while (!isClosed) {
T action = getQueue().dispatch(currentTime());
if (action == null) {
synchronized (threads) {
try {
long delay = getQueue().waitDelay(currentTime());
if (delay > 0) {
threads.wait(delay);
}
continue;
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
}
try {
dispatchMessage(action);
} catch (Throwable t) {
// Possibly danger situation, but i hope this will not corrupt JVM
// For example: on Android we could always continue execution after OutOfMemoryError
// Anyway, better to catch all errors manually in dispatchMessage
t.printStackTrace();
}
}
}
}
}

View File

@ -1,131 +0,0 @@
package com.droidkit.actors.mailbox;
import com.droidkit.actors.*;
import com.droidkit.actors.dispatch.AbstractDispatcher;
import com.droidkit.actors.messages.DeadLetter;
import com.droidkit.actors.messages.PoisonPill;
import com.droidkit.actors.messages.StartActor;
import java.util.HashMap;
import java.util.UUID;
/**
* Abstract Actor Dispatcher, used for dispatching messages for actors
*/
public abstract class AbsActorDispatcher {
private final HashMap<Mailbox, ActorScope> mailboxes = new HashMap<Mailbox, ActorScope>();
private final HashMap<String, ActorScope> scopes = new HashMap<String, ActorScope>();
private final HashMap<String, Props> actorProps = new HashMap<String, Props>();
private final ActorSystem actorSystem;
private AbstractDispatcher<Envelope, MailboxesQueue> dispatcher;
public AbsActorDispatcher(ActorSystem actorSystem) {
this.actorSystem = actorSystem;
}
protected void initDispatcher(AbstractDispatcher<Envelope, MailboxesQueue> dispatcher) {
if (this.dispatcher != null) {
throw new RuntimeException("Double dispatcher init");
}
this.dispatcher = dispatcher;
}
public final ActorScope createScope(String path, Props props) {
// TODO: add path check
Mailbox mailbox = new Mailbox(dispatcher.getQueue());
UUID uuid = UUID.randomUUID();
ActorRef ref = new ActorRef(actorSystem, this, uuid, path);
ActorScope scope = new ActorScope(actorSystem, mailbox, ref, this, UUID.randomUUID(), path, props);
synchronized (mailboxes) {
mailboxes.put(mailbox, scope);
scopes.put(scope.getPath(), scope);
actorProps.put(path, props);
}
// Sending init message
scope.getActorRef().send(StartActor.INSTANCE);
return scope;
}
public final void disconnectScope(ActorScope scope) {
synchronized (mailboxes) {
mailboxes.remove(scope.getMailbox());
scopes.remove(scope.getPath());
}
for (Envelope envelope : scope.getMailbox().allEnvelopes()) {
if (envelope.getSender() != null) {
envelope.getSender().send(new DeadLetter(envelope.getMessage()));
}
}
}
public final void sendMessage(String path, Object message, long time, ActorRef sender) {
synchronized (mailboxes) {
if (!scopes.containsKey(path)) {
if (sender != null) {
sender.send(new DeadLetter(message));
}
} else {
Mailbox mailbox = scopes.get(path).getMailbox();
mailbox.schedule(new Envelope(message, mailbox, sender), time);
}
}
}
public final void sendMessageOnce(String path, Object message, long time, ActorRef sender) {
synchronized (mailboxes) {
if (!scopes.containsKey(path)) {
if (sender != null) {
sender.send(new DeadLetter(message));
}
} else {
Mailbox mailbox = scopes.get(path).getMailbox();
mailbox.scheduleOnce(new Envelope(message, mailbox, sender), time);
}
}
}
/**
* Processing of envelope
*
* @param envelope envelope
*/
protected void processEnvelope(Envelope envelope) {
ActorScope actor = null;
synchronized (mailboxes) {
actor = mailboxes.get(envelope.getMailbox());
}
if (actor == null) {
//TODO: add logging
return;
}
try {
if (envelope.getMessage() == StartActor.INSTANCE) {
try {
actor.createActor();
} catch (Exception e) {
e.printStackTrace();
}
} else if (envelope.getMessage() == PoisonPill.INSTANCE) {
try {
actor.shutdownActor();
} catch (Exception e) {
e.printStackTrace();
}
} else {
CurrentActor.setCurrentActor(actor.getActor());
actor.setSender(envelope.getSender());
actor.getActor().onReceiveGlobal(envelope.getMessage());
}
} finally {
dispatcher.getQueue().unlockMailbox(envelope.getMailbox());
CurrentActor.setCurrentActor(null);
}
}
}

View File

@ -1,25 +0,0 @@
package com.droidkit.actors.mailbox;
import com.droidkit.actors.ActorSystem;
import com.droidkit.actors.dispatch.Dispatch;
import com.droidkit.actors.dispatch.ThreadPoolDispatcher;
/**
* Basic ActorDispatcher backed by ThreadPoolDispatcher
*/
public class ActorDispatcher extends AbsActorDispatcher {
public ActorDispatcher(ActorSystem actorSystem, int threadsCount) {
this(actorSystem, threadsCount, Thread.MIN_PRIORITY);
}
public ActorDispatcher(ActorSystem actorSystem, int threadsCount, int priority) {
super(actorSystem);
initDispatcher(new ThreadPoolDispatcher<Envelope, MailboxesQueue>(threadsCount, priority, new MailboxesQueue(),
new Dispatch<Envelope>() {
@Override
public void dispatchMessage(Envelope message) {
processEnvelope(message);
}
}));
}
}

View File

@ -1,54 +0,0 @@
package com.droidkit.actors.mailbox;
import com.droidkit.actors.ActorRef;
/**
* Actor system envelope
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class Envelope {
private final Object message;
private final ActorRef sender;
private final Mailbox mailbox;
/**
* Creating of envelope
*
* @param message message
* @param mailbox mailbox
* @param sender sender reference
*/
public Envelope(Object message, Mailbox mailbox, ActorRef sender) {
this.message = message;
this.sender = sender;
this.mailbox = mailbox;
}
/**
* Message in envelope
*
* @return message
*/
public Object getMessage() {
return message;
}
/**
* Mailbox for envelope
*
* @return mailbox
*/
public Mailbox getMailbox() {
return mailbox;
}
/**
* Sender of message
*
* @return sender reference
*/
public ActorRef getSender() {
return sender;
}
}

View File

@ -1,88 +0,0 @@
package com.droidkit.actors.mailbox;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Actor mailbox, queue of envelopes.
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class Mailbox {
private final ConcurrentHashMap<Long, Envelope> envelopes = new ConcurrentHashMap<Long, Envelope>();
private MailboxesQueue queue;
/**
* Creating mailbox
*
* @param queue MailboxesQueue
*/
public Mailbox(MailboxesQueue queue) {
this.queue = queue;
}
/**
* Send envelope at time
*
* @param envelope envelope
* @param time time
*/
public void schedule(Envelope envelope, long time) {
if (envelope.getMailbox() != this) {
throw new RuntimeException("envelope.mailbox != this mailbox");
}
long id = queue.sendEnvelope(envelope, time);
envelopes.put(id, envelope);
}
/**
* Send envelope once at time
*
* @param envelope envelope
* @param time time
*/
public void scheduleOnce(Envelope envelope, long time) {
if (envelope.getMailbox() != this) {
throw new RuntimeException("envelope.mailbox != this mailbox");
}
Iterator<Map.Entry<Long, Envelope>> iterator = envelopes.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Long, Envelope> entry = iterator.next();
if (isEqualEnvelope(entry.getValue(), envelope)) {
queue.removeEnvelope(entry.getKey());
iterator.remove();
}
}
schedule(envelope, time);
}
void removeEnvelope(long key) {
envelopes.remove(key);
}
/**
* Override this if you need to change filtering for scheduleOnce behaviour.
* By default it check equality only of class names.
*
* @param a
* @param b
* @return is equal
*/
protected boolean isEqualEnvelope(Envelope a, Envelope b) {
return a.getMessage().getClass() == b.getMessage().getClass();
}
public synchronized Envelope[] allEnvelopes() {
return envelopes.values().toArray(new Envelope[0]);
}
public synchronized int getMailboxSize() {
return envelopes.size();
}
}

View File

@ -1,145 +0,0 @@
package com.droidkit.actors.mailbox;
import com.droidkit.actors.dispatch.AbstractDispatchQueue;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* Queue of multiple mailboxes for MailboxesDispatcher
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class MailboxesQueue extends AbstractDispatchQueue<Envelope> {
private static final long MULTIPLE = 10000L;
private final TreeMap<Long, Long> timeShift = new TreeMap<Long, Long>();
private final TreeMap<Long, Envelope> envelopes = new TreeMap<Long, Envelope>();
private final HashSet<Mailbox> blocked = new HashSet<Mailbox>();
/**
* Locking mailbox from processing messages from it
*
* @param mailbox mailbox for locking
*/
public void lockMailbox(Mailbox mailbox) {
synchronized (blocked) {
blocked.add(mailbox);
}
notifyQueueChanged();
}
/**
* Unlocking mailbox
*
* @param mailbox mailbox for unlocking
*/
public void unlockMailbox(Mailbox mailbox) {
synchronized (blocked) {
blocked.remove(mailbox);
}
notifyQueueChanged();
}
/**
* Sending envelope
*
* @param envelope envelope
* @param time time (see {@link com.droidkit.actors.ActorTime#currentTime()}})
* @return envelope real time
*/
public long sendEnvelope(Envelope envelope, long time) {
long shift = 0;
synchronized (envelopes) {
if (timeShift.containsKey(time)) {
shift = timeShift.get(time);
}
while (envelopes.containsKey(time * MULTIPLE + shift)) {
shift++;
}
if (shift != 0) {
timeShift.put(time, shift);
}
envelopes.put(time * MULTIPLE + shift, envelope);
}
notifyQueueChanged();
return time * MULTIPLE + shift;
}
/**
* Removing envelope from queue
*
* @param id envelope id
*/
public void removeEnvelope(long id) {
synchronized (envelopes) {
envelopes.remove(id);
}
notifyQueueChanged();
}
/**
* getting first available envelope
* MUST BE wrapped with envelopes and blocked sync
*
* @return envelope entry
*/
private Map.Entry<Long, Envelope> firstEnvelope() {
for (Map.Entry<Long, Envelope> entry : envelopes.entrySet()) {
if (!blocked.contains(entry.getValue().getMailbox())) {
return entry;
}
}
return null;
}
@Override
public Envelope dispatch(long time) {
time = time * MULTIPLE;
synchronized (envelopes) {
synchronized (blocked) {
Map.Entry<Long, Envelope> envelope = firstEnvelope();
if (envelope != null) {
if (envelope.getKey() < time) {
envelopes.remove(envelope.getKey());
envelope.getValue().getMailbox().removeEnvelope(envelope.getKey());
//TODO: Better design
// Locking of mailbox before dispatch return
blocked.add(envelope.getValue().getMailbox());
return envelope.getValue();
}
}
}
}
return null;
}
@Override
public long waitDelay(long time) {
time = time * MULTIPLE;
synchronized (envelopes) {
synchronized (blocked) {
Map.Entry<Long, Envelope> envelope = firstEnvelope();
if (envelope != null) {
if (envelope.getKey() <= time) {
return 0;
} else {
return (time - envelope.getKey()) / MULTIPLE;
}
}
}
}
return FOREVER;
}
@Override
protected void putToQueueImpl(Envelope message, long atTime) {
sendEnvelope(message, atTime);
}
}

View File

@ -1,21 +0,0 @@
package com.droidkit.actors.messages;
/**
* DeadLetter sent whet message was not processed by target actor
*/
public class DeadLetter {
private Object message;
public DeadLetter(Object message) {
this.message = message;
}
public Object getMessage() {
return message;
}
@Override
public String toString() {
return "DeadLetter(" + message + ")";
}
}

View File

@ -1,22 +0,0 @@
package com.droidkit.actors.messages;
/**
* Simple named message
*/
public class NamedMessage {
private String name;
private Object message;
public NamedMessage(String name, Object message) {
this.name = name;
this.message = message;
}
public String getName() {
return name;
}
public Object getMessage() {
return message;
}
}

View File

@ -1,13 +0,0 @@
package com.droidkit.actors.messages;
/**
* PoisonPill message for killing actors
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public final class PoisonPill {
public static final PoisonPill INSTANCE = new PoisonPill();
private PoisonPill() {
}
}

View File

@ -1,13 +0,0 @@
package com.droidkit.actors.messages;
/**
* Message for starting actors
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public final class StartActor {
public static final StartActor INSTANCE = new StartActor();
private StartActor() {
}
}

View File

@ -1,165 +0,0 @@
package com.droidkit.actors.tasks;
import com.droidkit.actors.ActorRef;
import com.droidkit.actors.messages.DeadLetter;
import com.droidkit.actors.tasks.messages.*;
import java.util.HashMap;
/**
* Implementation of Ask pattern
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class ActorAskImpl {
private HashMap<Integer, AskContainer> asks = new HashMap<Integer, AskContainer>();
private int nextReqId = 1;
private ActorRef self;
public ActorAskImpl(ActorRef self) {
this.self = self;
}
public <T> AskFuture<T[]> combine(AskFuture... futures) {
final AskFuture resultFuture = new AskFuture(this, 0);
final CombineContainer container = new CombineContainer(futures.length);
for (int i = 0; i < futures.length; i++) {
final int index = i;
container.futures[index] = futures[index];
container.callbacks[index] = new AskCallback() {
@Override
public void onResult(Object result) {
container.completed[index] = true;
container.results[index] = result;
boolean isCompleted = true;
for (boolean c : container.completed) {
if (!c) {
isCompleted = false;
break;
}
}
if (isCompleted && !container.isCompleted) {
container.isCompleted = true;
for (int i = 0; i < container.futures.length; i++) {
container.futures[i].removeListener(container.callbacks[i]);
}
resultFuture.onResult(container.results);
}
}
@Override
public void onError(Throwable throwable) {
if (!container.isCompleted) {
container.isCompleted = true;
for (int i = 0; i < container.futures.length; i++) {
container.futures[i].removeListener(container.callbacks[i]);
container.futures[i].cancel();
}
resultFuture.onError(throwable);
}
}
};
container.futures[index].addListener(container.callbacks[index]);
}
return resultFuture;
}
public <T> AskFuture<T> ask(ActorRef ref, long timeout, AskCallback<T> callback) {
int reqId = nextReqId++;
AskFuture<T> future = new AskFuture<T>(this, reqId);
if (callback != null) {
future.addListener(callback);
}
AskContainer container = new AskContainer(future, ref, reqId);
asks.put(reqId, container);
ref.send(new TaskRequest(reqId), self);
if (timeout > 0) {
self.send(new TaskTimeout(reqId), timeout);
}
return future;
}
public boolean onTaskResult(TaskResult result) {
AskContainer container = asks.remove(result.getRequestId());
if (container != null) {
container.future.onResult(result.getRes());
return true;
}
return false;
}
public boolean onTaskError(TaskError error) {
AskContainer container = asks.remove(error.getRequestId());
if (container != null) {
container.future.onError(error.getThrowable());
return true;
}
return false;
}
public boolean onTaskTimeout(TaskTimeout taskTimeout) {
AskContainer container = asks.remove(taskTimeout.getRequestId());
if (container != null) {
container.future.onTimeout();
return true;
}
return false;
}
public boolean onTaskCancelled(int reqId) {
AskContainer container = asks.remove(reqId);
if (container != null) {
container.ref.send(new TaskCancel(reqId), self);
return true;
}
return false;
}
public boolean onDeadLetter(DeadLetter letter) {
if (letter.getMessage() instanceof TaskRequest) {
TaskRequest request = (TaskRequest) letter.getMessage();
AskContainer container = asks.remove(request.getRequestId());
if (container != null) {
// Mimic dead letter with timeout exception
container.future.onError(new AskTimeoutException());
return true;
}
}
return false;
}
private class AskContainer {
public final AskFuture future;
public final ActorRef ref;
public final int requestId;
private AskContainer(AskFuture future, ActorRef ref, int requestId) {
this.future = future;
this.ref = ref;
this.requestId = requestId;
}
}
private class CombineContainer {
public boolean isCompleted = false;
public Object[] results;
public boolean[] completed;
public AskFuture[] futures;
public AskCallback[] callbacks;
public CombineContainer(int count) {
results = new Object[count];
completed = new boolean[count];
callbacks = new AskCallback[count];
futures = new AskFuture[count];
}
}
}

View File

@ -1,12 +0,0 @@
package com.droidkit.actors.tasks;
/**
* Callback for Ask pattern
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public interface AskCallback<T> {
public void onResult(T result);
public void onError(Throwable throwable);
}

View File

@ -1,10 +0,0 @@
package com.droidkit.actors.tasks;
/**
* Exception about cancelling task
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class AskCancelledException extends Exception {
}

View File

@ -1,105 +0,0 @@
package com.droidkit.actors.tasks;
import java.util.LinkedList;
/**
* Modified future for ask pattern of Actors
* Created to work only in actor threads
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class AskFuture<T> {
private LinkedList<AskCallback> callbacks = new LinkedList<AskCallback>();
private ActorAskImpl askImpl;
private int reqId;
private boolean isCompleted = false;
private boolean isCanceled = false;
private boolean isError = false;
private T result = null;
private Throwable error = null;
AskFuture(ActorAskImpl askImpl, int reqId) {
this.askImpl = askImpl;
this.reqId = reqId;
}
public void addListener(AskCallback callback) {
callbacks.add(callback);
}
public void removeListener(AskCallback callback) {
callbacks.remove(callback);
}
public boolean isCompleted() {
return isCompleted;
}
public boolean isError() {
return isError;
}
public boolean isCanceled() {
return isCanceled;
}
public Throwable error() {
return error;
}
public T result() {
return result;
}
public void cancel() {
if (isCompleted) {
return;
}
isCompleted = true;
isError = false;
isCanceled = true;
for (AskCallback callback : callbacks) {
callback.onError(new AskCancelledException());
}
askImpl.onTaskCancelled(reqId);
}
void onError(Throwable throwable) {
if (isCompleted) {
return;
}
isCompleted = true;
isCanceled = false;
isError = true;
error = throwable;
result = null;
for (AskCallback callback : callbacks) {
callback.onError(throwable);
}
}
void onResult(T res) {
if (isCompleted) {
return;
}
isCompleted = true;
isCanceled = false;
isError = false;
error = null;
result = res;
for (AskCallback callback : callbacks) {
callback.onResult(res);
}
}
void onTimeout() {
onError(new AskTimeoutException());
}
}

View File

@ -1,9 +0,0 @@
package com.droidkit.actors.tasks;
/**
* Exception for Ask timeout
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class AskTimeoutException extends Exception {
}

View File

@ -1,177 +0,0 @@
package com.droidkit.actors.tasks;
import com.droidkit.actors.Actor;
import com.droidkit.actors.ActorRef;
import com.droidkit.actors.messages.PoisonPill;
import com.droidkit.actors.tasks.messages.TaskCancel;
import com.droidkit.actors.tasks.messages.TaskError;
import com.droidkit.actors.tasks.messages.TaskRequest;
import com.droidkit.actors.tasks.messages.TaskResult;
import java.util.HashSet;
/**
* Actor for performing various async tasks
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public abstract class TaskActor<T> extends Actor {
private final HashSet<TaskListener> requests = new HashSet<TaskListener>();
private T result;
private boolean isCompleted;
private boolean isCompletedSuccess;
private long dieTimeout = 300;
/**
* Timeout for dying after task complete
*
* @param timeOut timeout in ms
*/
public void setTimeOut(long timeOut) {
dieTimeout = timeOut;
}
@Override
public void preStart() {
startTask();
}
@Override
public void onReceive(Object message) {
if (message instanceof TaskRequest) {
TaskRequest request = (TaskRequest) message;
if (isCompleted) {
if (isCompletedSuccess) {
reply(result);
}
} else {
TaskListener listener = new TaskListener(request.getRequestId(), sender());
requests.add(listener);
}
} else if (message instanceof TaskCancel) {
if (isCompleted) {
return;
}
TaskCancel cancel = (TaskCancel) message;
TaskListener listener = new TaskListener(cancel.getRequestId(), sender());
requests.remove(listener);
if (requests.size() == 0) {
onTaskObsolete();
context().stopSelf();
}
} else if (message instanceof Result) {
if (!isCompleted) {
Result res = (Result) message;
isCompleted = true;
isCompletedSuccess = true;
result = (T) res.getRes();
for (TaskListener request : requests) {
request.getSender().send(new TaskResult<T>(request.getRequestId(), result));
}
self().send(PoisonPill.INSTANCE, dieTimeout);
}
} else if (message instanceof Error) {
if (!isCompleted) {
isCompleted = true;
Error error = (Error) message;
for (TaskListener request : requests) {
request.getSender().send(new TaskError(request.getRequestId(), error.getError()));
}
context().stopSelf();
}
}
}
/**
* Starting of task execution
*/
public abstract void startTask();
/**
* Called before killing actor after clearing TaskListeners
*/
public void onTaskObsolete() {
}
/**
* Call this method in any thread after task complete
*
* @param res result of task
*/
public void complete(T res) {
self().send(new Result(res));
}
/**
* Call this method in any thread after task exception
*
* @param t exception
*/
public void error(Throwable t) {
self().send(new Error(t));
}
private static class Error {
private Throwable error;
private Error(Throwable error) {
this.error = error;
}
public Throwable getError() {
return error;
}
}
private static class Result {
private Object res;
private Result(Object res) {
this.res = res;
}
public Object getRes() {
return res;
}
}
private static class TaskListener {
private int requestId;
private ActorRef sender;
private TaskListener(int requestId, ActorRef sender) {
this.requestId = requestId;
this.sender = sender;
}
public int getRequestId() {
return requestId;
}
public ActorRef getSender() {
return sender;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TaskListener that = (TaskListener) o;
if (requestId != that.requestId) return false;
if (!sender.equals(that.sender)) return false;
return true;
}
@Override
public int hashCode() {
int result = requestId;
result = 31 * result + sender.hashCode();
return result;
}
}
}

View File

@ -1,18 +0,0 @@
package com.droidkit.actors.tasks.messages;
/**
* Message about task cancelling
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class TaskCancel {
private int requestId;
public TaskCancel(int requestId) {
this.requestId = requestId;
}
public int getRequestId() {
return requestId;
}
}

View File

@ -1,24 +0,0 @@
package com.droidkit.actors.tasks.messages;
/**
* Message about task error
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class TaskError {
private final int requestId;
private final Throwable throwable;
public TaskError(int requestId, Throwable throwable) {
this.requestId = requestId;
this.throwable = throwable;
}
public int getRequestId() {
return requestId;
}
public Throwable getThrowable() {
return throwable;
}
}

View File

@ -1,18 +0,0 @@
package com.droidkit.actors.tasks.messages;
/**
* Message about requesting task
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class TaskRequest {
private final int requestId;
public TaskRequest(int requestId) {
this.requestId = requestId;
}
public int getRequestId() {
return requestId;
}
}

View File

@ -1,25 +0,0 @@
package com.droidkit.actors.tasks.messages;
/**
* Message with task result
*
* @param <T> type of task result
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class TaskResult<T> {
private final int requestId;
private final T res;
public TaskResult(int requestId, T res) {
this.requestId = requestId;
this.res = res;
}
public T getRes() {
return res;
}
public int getRequestId() {
return requestId;
}
}

View File

@ -1,18 +0,0 @@
package com.droidkit.actors.tasks.messages;
/**
* Message about Task timeout
*
* @author Stepan Ex3NDR Korshakov (me@ex3ndr.com)
*/
public class TaskTimeout {
private final int requestId;
public TaskTimeout(int requestId) {
this.requestId = requestId;
}
public int getRequestId() {
return requestId;
}
}

View File

@ -1,17 +0,0 @@
package org.telegram.api.engine;
import org.telegram.api.TLAbsUpdates;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 11.11.13
* Time: 7:42
*/
public interface ApiCallback {
public void onAuthCancelled(TelegramApi api);
public void onUpdatesInvalidated(TelegramApi api);
public void onUpdate(TLAbsUpdates updates);
}

View File

@ -1,43 +0,0 @@
package org.telegram.api.engine;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 10.11.13
* Time: 2:31
*/
public class AppInfo {
protected int apiId;
protected String deviceModel;
protected String systemVersion;
protected String appVersion;
protected String langCode;
public AppInfo(int apiId, String deviceModel, String systemVersion, String appVersion, String langCode) {
this.apiId = apiId;
this.deviceModel = deviceModel;
this.systemVersion = systemVersion;
this.appVersion = appVersion;
this.langCode = langCode;
}
public int getApiId() {
return apiId;
}
public String getDeviceModel() {
return deviceModel;
}
public String getSystemVersion() {
return systemVersion;
}
public String getAppVersion() {
return appVersion;
}
public String getLangCode() {
return langCode;
}
}

View File

@ -1,60 +0,0 @@
package org.telegram.api.engine;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLGzipObject;
import org.telegram.tl.TLMethod;
import org.telegram.tl.TLObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
import static org.telegram.tl.StreamingUtils.*;
import static org.telegram.tl.StreamingUtils.writeByteArray;
/**
* Created by ex3ndr on 07.12.13.
*/
public class GzipRequest<T extends TLObject> extends TLMethod<T> {
private static final int CLASS_ID = TLGzipObject.CLASS_ID;
private TLMethod<T> method;
public GzipRequest(TLMethod<T> method) {
this.method = method;
}
@Override
public T deserializeResponse(InputStream stream, TLContext context) throws IOException {
return method.deserializeResponse(stream, context);
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
ByteArrayOutputStream resOutput = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(resOutput);
method.serialize(gzipOutputStream);
gzipOutputStream.flush();
gzipOutputStream.close();
byte[] body = resOutput.toByteArray();
writeTLBytes(body, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
throw new IOException("Unsupported operation");
}
@Override
public String toString() {
return "gzip<" + method + ">";
}
}

View File

@ -1,39 +0,0 @@
package org.telegram.api.engine;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 11.11.13
* Time: 4:48
*/
public class Logger {
private static LoggerInterface logInterface;
public static void registerInterface(LoggerInterface logInterface) {
Logger.logInterface = logInterface;
}
public static void w(String tag, String message) {
if (logInterface != null) {
logInterface.w(tag, message);
} else {
System.out.println(tag + ":" + message);
}
}
public static void d(String tag, String message) {
if (logInterface != null) {
logInterface.d(tag, message);
} else {
System.out.println(tag + ":" + message);
}
}
public static void e(String tag, Throwable t) {
if (logInterface != null) {
logInterface.e(tag, t);
} else {
t.printStackTrace();
}
}
}

View File

@ -1,15 +0,0 @@
package org.telegram.api.engine;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 11.11.13
* Time: 4:48
*/
public interface LoggerInterface {
void w(String tag, String message);
void d(String tag, String message);
void e(String tag, Throwable t);
}

View File

@ -1,15 +0,0 @@
package org.telegram.api.engine;
import org.telegram.tl.TLObject;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 05.11.13
* Time: 14:10
*/
public interface RpcCallback<T extends TLObject> {
public void onResult(T result);
public void onError(int errorCode, String message);
}

View File

@ -1,13 +0,0 @@
package org.telegram.api.engine;
import org.telegram.tl.TLObject;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 09.11.13
* Time: 18:06
*/
public interface RpcCallbackEx<T extends TLObject> extends RpcCallback<T> {
public void onConfirmed();
}

View File

@ -1,56 +0,0 @@
package org.telegram.api.engine;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 05.11.13
* Time: 13:59
*/
public class RpcException extends IOException {
private static final Pattern REGEXP_PATTERN = Pattern.compile("[A-Z_0-9]+");
private static String getErrorTag(String srcMessage) {
if (srcMessage == null) {
return "UNKNOWN";
}
Matcher matcher = REGEXP_PATTERN.matcher(srcMessage);
if (matcher.find()) {
return matcher.group();
}
return "UNKNOWN";
}
private static String getErrorMessage(String srcMessage) {
if (srcMessage == null) {
return "Unknown error";
}
int index = srcMessage.indexOf(":");
if (index > 0) {
return srcMessage.substring(index);
} else {
return srcMessage;
}
}
private int errorCode;
private String errorTag;
public RpcException(int errorCode, String message) {
super(getErrorMessage(message));
this.errorCode = errorCode;
this.errorTag = getErrorTag(message);
}
public int getErrorCode() {
return errorCode;
}
public String getErrorTag() {
return errorTag;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
package org.telegram.api.engine;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 06.11.13
* Time: 1:27
*/
public class TimeoutException extends IOException {
public TimeoutException() {
}
public TimeoutException(String s) {
super(s);
}
public TimeoutException(String s, Throwable throwable) {
super(s, throwable);
}
public TimeoutException(Throwable throwable) {
super(throwable);
}
}

View File

@ -1,12 +0,0 @@
package org.telegram.api.engine.file;
/**
* Created by ex3ndr on 18.11.13.
*/
public interface DownloadListener {
public void onPartDownloaded(int percent, int downloadedSize);
public void onDownloaded();
public void onFailed();
}

View File

@ -1,368 +0,0 @@
package org.telegram.api.engine.file;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import org.telegram.api.TLAbsInputFileLocation;
import org.telegram.api.engine.Logger;
import org.telegram.api.engine.TelegramApi;
import org.telegram.api.upload.TLFile;
/**
* Created by ex3ndr on 18.11.13.
*/
public class Downloader {
public static final int FILE_QUEUED = 0;
public static final int FILE_DOWNLOADING = 1;
public static final int FILE_COMPLETED = 2;
public static final int FILE_CANCELED = 3;
public static final int FILE_FAILURE = 4;
private final AtomicInteger fileIds = new AtomicInteger(1);
private final String TAG;
private TelegramApi api;
private static final long DOWNLOAD_TIMEOUT = 30 * 1000;
private static final long DEFAULT_DELAY = 15 * 1000;
private static final int BLOCK_SIZE = 16 * 1024;
private static final int PARALLEL_DOWNLOAD_COUNT = 2;
private static final int PARALLEL_PARTS_COUNT = 4;
private static final int BLOCK_QUEUED = 0;
private static final int BLOCK_DOWNLOADING = 1;
private static final int BLOCK_COMPLETED = 2;
private ArrayList<DownloadTask> tasks = new ArrayList<DownloadTask>();
private ArrayList<DownloadFileThread> threads = new ArrayList<DownloadFileThread>();
private final Object threadLocker = new Object();
private Random rnd = new Random();
public Downloader(TelegramApi api) {
this.TAG = api.toString() + "#Downloader";
this.api = api;
for (int i = 0; i < PARALLEL_PARTS_COUNT; i++) {
DownloadFileThread thread = new DownloadFileThread();
thread.start();
threads.add(thread);
}
}
public TelegramApi getApi() {
return api;
}
private synchronized DownloadTask getTask(int taskId) {
for (DownloadTask task : tasks) {
if (task.taskId == taskId) {
return task;
}
}
return null;
}
public synchronized void cancelTask(int taskId) {
DownloadTask task = getTask(taskId);
if (task != null && task.state != FILE_COMPLETED) {
task.state = FILE_CANCELED;
Logger.d(TAG, "File #" + task.taskId + "| Canceled");
}
updateFileQueueStates();
}
public synchronized int getTaskState(int taskId) {
DownloadTask task = getTask(taskId);
if (task != null) {
return task.state;
}
return FILE_CANCELED;
}
public void waitForTask(int taskId) {
while (true) {
int state = getTaskState(taskId);
if ((state == FILE_COMPLETED) || (state == FILE_FAILURE) || (state == FILE_CANCELED)) {
return;
}
synchronized (threadLocker) {
try {
threadLocker.wait(DEFAULT_DELAY);
} catch (InterruptedException e) {
Logger.e(TAG, e);
return;
}
}
}
}
public synchronized int requestTask(int dcId, TLAbsInputFileLocation location, int size, String destFile, DownloadListener listener) {
int blockSize = BLOCK_SIZE;
int totalBlockCount = (int) Math.ceil(((double) size) / blockSize);
DownloadTask task = new DownloadTask();
task.listener = listener;
task.blockSize = blockSize;
task.destFile = destFile;
try {
task.file = new RandomAccessFile(destFile, "rw");
task.file.setLength(size);
} catch (FileNotFoundException e) {
Logger.e(TAG, e);
} catch (IOException e) {
Logger.e(TAG, e);
}
task.taskId = fileIds.getAndIncrement();
task.dcId = dcId;
task.location = location;
task.size = size;
task.blocks = new DownloadBlock[totalBlockCount];
for (int i = 0; i < totalBlockCount; i++) {
task.blocks[i] = new DownloadBlock();
task.blocks[i].task = task;
task.blocks[i].index = i;
task.blocks[i].state = BLOCK_QUEUED;
}
task.state = FILE_QUEUED;
task.queueTime = System.nanoTime();
tasks.add(task);
Logger.d(TAG, "File #" + task.taskId + "| Requested");
updateFileQueueStates();
return task.taskId;
}
private synchronized DownloadTask[] getActiveTasks() {
ArrayList<DownloadTask> res = new ArrayList<DownloadTask>();
for (DownloadTask task : tasks) {
if (task.state == FILE_DOWNLOADING) {
res.add(task);
}
}
return res.toArray(new DownloadTask[res.size()]);
}
private synchronized void updateFileQueueStates() {
DownloadTask[] activeTasks = getActiveTasks();
outer:
for (DownloadTask task : activeTasks) {
for (DownloadBlock block : task.blocks) {
if (block.state != BLOCK_COMPLETED) {
continue outer;
}
}
onTaskCompleted(task);
}
activeTasks = getActiveTasks();
int count = activeTasks.length;
while (count < PARALLEL_DOWNLOAD_COUNT) {
long mintime = Long.MAX_VALUE;
DownloadTask minTask = null;
for (DownloadTask task : tasks) {
if (task.state == FILE_QUEUED && task.queueTime < mintime) {
minTask = task;
}
}
if (minTask == null) {
break;
}
minTask.state = FILE_DOWNLOADING;
Logger.d(TAG, "File #" + minTask.taskId + "| Downloading");
}
synchronized (threadLocker) {
threadLocker.notifyAll();
}
}
private synchronized void onTaskCompleted(DownloadTask task) {
if (task.state != FILE_COMPLETED) {
Logger.d(TAG, "File #" + task.taskId + "| Completed in " + (System.nanoTime() - task.queueTime) / (1000 * 1000L) + " ms");
task.state = FILE_COMPLETED;
try {
if (task.file != null) {
task.file.close();
task.file = null;
}
} catch (IOException e) {
Logger.e(TAG, e);
}
}
updateFileQueueStates();
}
private synchronized void onTaskFailure(DownloadTask task) {
if (task.state != FILE_FAILURE) {
Logger.d(TAG, "File #" + task.taskId + "| Failure in " + (System.nanoTime() - task.queueTime) / (1000 * 1000L) + " ms");
task.state = FILE_FAILURE;
try {
if (task.file != null) {
task.file.close();
task.file = null;
}
} catch (IOException e) {
Logger.e(TAG, e);
}
}
updateFileQueueStates();
}
private synchronized DownloadTask fetchTask() {
DownloadTask[] activeTasks = getActiveTasks();
if (activeTasks.length == 0) {
return null;
} else if (activeTasks.length == 1) {
return activeTasks[0];
} else {
return activeTasks[rnd.nextInt(activeTasks.length)];
}
}
private synchronized DownloadBlock fetchBlock() {
DownloadTask task = fetchTask();
if (task == null) {
return null;
}
for (int i = 0; i < task.blocks.length; i++) {
if (task.blocks[i].state == BLOCK_QUEUED) {
task.blocks[i].state = BLOCK_DOWNLOADING;
if (task.lastSuccessBlock == 0) {
task.lastSuccessBlock = System.nanoTime();
}
return task.blocks[i];
}
}
return null;
}
private synchronized void onBlockDownloaded(DownloadBlock block, byte[] data) {
try {
if (block.task.file != null) {
block.task.file.seek(block.index * block.task.blockSize);
block.task.file.write(data);
} else {
return;
}
} catch (IOException e) {
Logger.e(TAG, e);
}
block.task.lastSuccessBlock = System.nanoTime();
block.state = BLOCK_COMPLETED;
if (block.task.listener != null) {
int downloadedCount = 0;
for (DownloadBlock b : block.task.blocks) {
if (b.state == BLOCK_COMPLETED) {
downloadedCount++;
}
}
int percent = downloadedCount * 100 / block.task.blocks.length;
block.task.listener.onPartDownloaded(percent, downloadedCount);
}
updateFileQueueStates();
}
private synchronized void onBlockFailure(DownloadBlock block) {
block.state = BLOCK_QUEUED;
if (block.task.lastSuccessBlock != 0 && (System.nanoTime() - block.task.lastSuccessBlock > DOWNLOAD_TIMEOUT * 1000L * 1000L)) {
onTaskFailure(block.task);
}
updateFileQueueStates();
}
private class DownloadTask {
public DownloadListener listener;
public long lastNotifyTime;
public int taskId;
public int blockSize;
public int dcId;
public TLAbsInputFileLocation location;
public int size;
public long queueTime;
public int state;
public DownloadBlock[] blocks;
public String destFile;
public RandomAccessFile file;
public long lastSuccessBlock;
}
private class DownloadBlock {
public DownloadTask task;
public int state;
public int index;
}
private class DownloadFileThread extends Thread {
public DownloadFileThread() {
setName("DownloadFileThread#" + hashCode());
}
@Override
public void run() {
setPriority(Thread.MIN_PRIORITY);
while (true) {
Logger.d(TAG, "DownloadFileThread iteration");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
DownloadBlock block = fetchBlock();
if (block == null) {
synchronized (threadLocker) {
try {
threadLocker.wait();
continue;
} catch (InterruptedException e) {
Logger.e(TAG, e);
return;
}
}
}
long start = System.nanoTime();
Logger.d(TAG, "Block #" + block.index + " of #" + block.task.taskId + "| Starting");
try {
TLFile file = api.doGetFile(block.task.dcId, block.task.location, block.index * block.task.blockSize, block.task.blockSize);
Logger.d(TAG, "Block #" + block.index + " of #" + block.task.taskId + "| Downloaded in " + (System.nanoTime() - start) / (1000 * 1000L) + " ms");
onBlockDownloaded(block, file.getBytes());
} catch (IOException e) {
Logger.d(TAG, "Block #" + block.index + " of #" + block.task.taskId + "| Failure");
Logger.e(TAG, e);
onBlockFailure(block);
}
}
}
}
}

View File

@ -1,8 +0,0 @@
package org.telegram.api.engine.file;
/**
* Created by ex3ndr on 19.11.13.
*/
public interface UploadListener {
public void onPartUploaded(int percent, int downloadedSize);
}

View File

@ -1,413 +0,0 @@
package org.telegram.api.engine.file;
import org.telegram.api.engine.Logger;
import org.telegram.api.engine.TelegramApi;
import org.telegram.mtproto.secure.CryptoUtils;
import org.telegram.mtproto.secure.Entropy;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by ex3ndr on 19.11.13.
*/
public class Uploader {
private static final int KB = 1024;
private static final int MB = 1024 * KB;
private final AtomicInteger fileIds = new AtomicInteger(1);
public static final int FILE_QUEUED = 0;
public static final int FILE_IN_PROGRESS = 1;
public static final int FILE_COMPLETED = 2;
public static final int FILE_CANCELED = 3;
private static final int BLOCK_QUEUED = 0;
private static final int BLOCK_DOWNLOADING = 1;
private static final int BLOCK_COMPLETED = 2;
private static final int PARALLEL_DOWNLOAD_COUNT = 2;
private static final int PARALLEL_PARTS_COUNT = 4;
private static final int[] BLOCK_SIZES = new int[]{8 * KB, 16 * KB, 32 * KB, 64 * KB, 128 * KB, 256 * KB, 512 * KB};
private static final long DEFAULT_DELAY = 15 * 1000;
private static final int BIG_FILE_MIN = 10 * 1024 * 1024;
private static final int MAX_BLOCK_COUNT = 3000;
private final String TAG;
private TelegramApi api;
private ArrayList<UploadTask> tasks = new ArrayList<UploadTask>();
private ArrayList<UploadFileThread> threads = new ArrayList<UploadFileThread>();
private final Object threadLocker = new Object();
private Random rnd = new Random();
public Uploader(TelegramApi api) {
this.TAG = api.toString() + "#Uploader";
this.api = api;
for (int i = 0; i < PARALLEL_PARTS_COUNT; i++) {
UploadFileThread thread = new UploadFileThread();
thread.start();
threads.add(thread);
}
}
public TelegramApi getApi() {
return api;
}
private synchronized UploadTask getTask(int taskId) {
for (UploadTask task : tasks) {
if (task.taskId == taskId) {
return task;
}
}
return null;
}
public synchronized void cancelTask(int taskId) {
UploadTask task = getTask(taskId);
if (task != null && task.state != FILE_COMPLETED) {
task.state = FILE_CANCELED;
Logger.d(TAG, "File #" + task.taskId + "| Canceled");
}
updateFileQueueStates();
}
public synchronized int getTaskState(int taskId) {
UploadTask task = getTask(taskId);
if (task != null) {
return task.state;
}
return FILE_CANCELED;
}
public void waitForTask(int taskId) {
while (true) {
int state = getTaskState(taskId);
if ((state == FILE_COMPLETED) || (state == FILE_CANCELED)) {
return;
}
synchronized (threadLocker) {
try {
threadLocker.wait(DEFAULT_DELAY);
} catch (InterruptedException e) {
Logger.e(TAG, e);
return;
}
}
}
}
public UploadResult getUploadResult(int taskId) {
UploadTask task = getTask(taskId);
if (task == null) {
return null;
}
if (task.state != FILE_COMPLETED) {
return null;
}
return new UploadResult(task.uniqId, task.blocks.length, task.hash, task.usedBigFile);
}
public synchronized int requestTask(String srcFile, UploadListener listener) {
UploadTask task = new UploadTask();
task.taskId = fileIds.getAndIncrement();
task.uniqId = Entropy.generateRandomId();
task.listener = listener;
task.srcFile = srcFile;
try {
task.file = new RandomAccessFile(srcFile, "r");
task.size = (int) task.file.length();
if (task.size >= BIG_FILE_MIN) {
task.usedBigFile = true;
Logger.d(TAG, "File #" + task.uniqId + "| Using big file method");
} else {
task.usedBigFile = false;
}
long start = System.currentTimeMillis();
Logger.d(TAG, "File #" + task.uniqId + "| Calculating hash");
task.hash = CryptoUtils.MD5(task.file);
Logger.d(TAG, "File #" + task.uniqId + "| Hash " + task.hash + " in " + (System.currentTimeMillis() - start) + " ms");
} catch (FileNotFoundException e) {
Logger.e(TAG, e);
} catch (IOException e) {
Logger.e(TAG, e);
}
task.blockSize = BLOCK_SIZES[BLOCK_SIZES.length - 1];
for (int size : BLOCK_SIZES) {
int totalBlockCount = (int) Math.ceil(((double) task.size) / size);
if (totalBlockCount < MAX_BLOCK_COUNT) {
task.blockSize = size;
break;
}
}
Logger.d(TAG, "File #" + task.uniqId + "| Using block size: " + task.blockSize);
int totalBlockCount = (int) Math.ceil(((double) task.size) / task.blockSize);
task.blocks = new UploadBlock[totalBlockCount];
for (int i = 0; i < totalBlockCount; i++) {
task.blocks[i] = new UploadBlock();
task.blocks[i].task = task;
task.blocks[i].index = i;
task.blocks[i].state = BLOCK_QUEUED;
}
task.state = FILE_QUEUED;
task.queueTime = System.nanoTime();
tasks.add(task);
Logger.d(TAG, "File #" + task.uniqId + "| Requested");
updateFileQueueStates();
return task.taskId;
}
private synchronized UploadTask[] getActiveTasks() {
ArrayList<UploadTask> res = new ArrayList<UploadTask>();
for (UploadTask task : tasks) {
if (task.state == FILE_IN_PROGRESS) {
res.add(task);
}
}
return res.toArray(new UploadTask[res.size()]);
}
private synchronized void updateFileQueueStates() {
UploadTask[] activeTasks = getActiveTasks();
outer:
for (UploadTask task : activeTasks) {
for (UploadBlock block : task.blocks) {
if (block.state != BLOCK_COMPLETED) {
continue outer;
}
}
onTaskCompleted(task);
}
activeTasks = getActiveTasks();
int count = activeTasks.length;
while (count < PARALLEL_DOWNLOAD_COUNT) {
long mintime = Long.MAX_VALUE;
UploadTask minTask = null;
for (UploadTask task : tasks) {
if (task.state == FILE_QUEUED && task.queueTime < mintime) {
minTask = task;
}
}
if (minTask == null) {
break;
}
minTask.state = FILE_IN_PROGRESS;
Logger.d(TAG, "File #" + minTask.uniqId + "| Uploading");
}
synchronized (threadLocker) {
threadLocker.notifyAll();
}
}
private synchronized void onTaskCompleted(UploadTask task) {
if (task.state != FILE_COMPLETED) {
Logger.d(TAG, "File #" + task.uniqId + "| Completed in " + (System.nanoTime() - task.queueTime) / (1000 * 1000L) + " ms");
task.state = FILE_COMPLETED;
try {
if (task.file != null) {
task.file.close();
task.file = null;
}
} catch (IOException e) {
Logger.e(TAG, e);
}
}
updateFileQueueStates();
}
private synchronized UploadTask fetchTask() {
UploadTask[] activeTasks = getActiveTasks();
if (activeTasks.length == 0) {
return null;
} else if (activeTasks.length == 1) {
return activeTasks[0];
} else {
return activeTasks[rnd.nextInt(activeTasks.length)];
}
}
private synchronized UploadBlock fetchBlock() {
UploadTask task = fetchTask();
if (task == null) {
return null;
}
for (int i = 0; i < task.blocks.length; i++) {
if (task.blocks[i].state == BLOCK_QUEUED) {
task.blocks[i].state = BLOCK_DOWNLOADING;
byte[] block = new byte[Math.min(task.size - task.blockSize * i, task.blockSize)];
try {
task.file.seek(task.blockSize * i);
task.file.readFully(block);
} catch (IOException e) {
Logger.e(TAG, e);
}
task.blocks[i].workData = block;
return task.blocks[i];
}
}
return null;
}
private synchronized void onBlockUploaded(UploadBlock block) {
block.state = BLOCK_COMPLETED;
if (block.task.listener != null) {
int downloadedCount = 0;
for (UploadBlock b : block.task.blocks) {
if (b.state == BLOCK_COMPLETED) {
downloadedCount++;
}
}
int percent = downloadedCount * 100 / block.task.blocks.length;
block.task.listener.onPartUploaded(percent, downloadedCount);
}
updateFileQueueStates();
}
private synchronized void onBlockFailure(UploadBlock block) {
block.state = BLOCK_QUEUED;
updateFileQueueStates();
}
public static class UploadResult {
private long fileId;
private boolean usedBigFile;
private int partsCount;
private String hash;
public UploadResult(long fileId, int partsCount, String hash, boolean usedBigFile) {
this.fileId = fileId;
this.partsCount = partsCount;
this.hash = hash;
this.usedBigFile = usedBigFile;
}
public long getFileId() {
return fileId;
}
public boolean isUsedBigFile() {
return usedBigFile;
}
public int getPartsCount() {
return partsCount;
}
public String getHash() {
return hash;
}
}
private class UploadTask {
public UploadListener listener;
public boolean usedBigFile;
public long uniqId;
public int taskId;
public int blockSize;
public long queueTime;
public int state;
public int size;
public UploadBlock[] blocks;
public String srcFile;
public RandomAccessFile file;
public String hash;
}
private class UploadBlock {
public UploadTask task;
public int state;
public int index;
public byte[] workData;
}
private class UploadFileThread extends Thread {
public UploadFileThread() {
setName("UploadFileThread#" + hashCode());
}
@Override
public void run() {
setPriority(Thread.MIN_PRIORITY);
while (true) {
Logger.d(TAG, "UploadFileThread iteration");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
UploadBlock block = fetchBlock();
if (block == null) {
synchronized (threadLocker) {
try {
threadLocker.wait();
continue;
} catch (InterruptedException e) {
Logger.e(TAG, e);
return;
}
}
}
long start = System.nanoTime();
Logger.d(TAG, "Block #" + block.index + " of #" + block.task.uniqId + "| Starting");
try {
if (block.task.usedBigFile) {
api.doSaveBigFilePart(block.task.uniqId, block.index, block.task.blocks.length, block.workData);
} else {
api.doSaveFilePart(block.task.uniqId, block.index, block.workData);
}
block.workData = null;
Logger.d(TAG, "Block #" + block.index + " of #" + block.task.uniqId + "| Uploaded in " + (System.nanoTime() - start) / (1000 * 1000L) + " ms");
onBlockUploaded(block);
} catch (IOException e) {
Logger.d(TAG, "Block #" + block.index + " of #" + block.task.uniqId + "| Failure");
Logger.e(TAG, e);
onBlockFailure(block);
}
}
}
}
}

View File

@ -1,41 +0,0 @@
package org.telegram.api.engine.storage;
import org.telegram.api.TLConfig;
import org.telegram.mtproto.state.AbsMTProtoState;
import org.telegram.mtproto.state.ConnectionInfo;
import org.telegram.mtproto.state.KnownSalt;
import java.util.HashMap;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 10:19
*/
public interface AbsApiState {
int getPrimaryDc();
public void setPrimaryDc(int dc);
boolean isAuthenticated(int dcId);
void setAuthenticated(int dcId, boolean auth);
void updateSettings(TLConfig config);
byte[] getAuthKey(int dcId);
void putAuthKey(int dcId, byte[] key);
ConnectionInfo[] getAvailableConnections(int dcId);
AbsMTProtoState getMtProtoState(int dcId);
void resetAuth();
void reset();
}

View File

@ -1,14 +0,0 @@
package org.telegram.mtproto;
import org.telegram.tl.TLMethod;
import org.telegram.tl.TLObject;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 3:56
*/
public interface CallWrapper {
public TLObject wrapObject(TLMethod srcRequest);
}

View File

@ -1,561 +0,0 @@
package org.telegram.mtproto;
import com.droidkit.actors.*;
import org.telegram.mtproto.log.Logger;
import org.telegram.mtproto.schedule.Scheduller;
import org.telegram.mtproto.secure.Entropy;
import org.telegram.mtproto.state.AbsMTProtoState;
import org.telegram.mtproto.state.KnownSalt;
import org.telegram.mtproto.time.TimeOverlord;
import org.telegram.mtproto.tl.*;
import org.telegram.mtproto.transport.*;
import org.telegram.mtproto.util.BytesCache;
import org.telegram.tl.DeserializeException;
import org.telegram.tl.TLMethod;
import org.telegram.tl.TLObject;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import static org.telegram.mtproto.secure.CryptoUtils.*;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:14
*/
public class MTProto {
public static final int MODE_GENERAL = 0;
public static final int MODE_GENERAL_LOW_MODE = 1;
public static final int MODE_FILE = 2;
public static final int MODE_PUSH = 3;
private static final AtomicInteger instanceIndex = new AtomicInteger(1000);
private static final int MESSAGES_CACHE = 100;
private static final int MESSAGES_CACHE_MIN = 10;
private static final int MAX_INTERNAL_FLOOD_WAIT = 10;//10 sec
private static final int PING_INTERVAL_REQUEST = 60000;// 1 min
private static final int PING_INTERVAL = 75;//75 secs
private static final int PING_PUSH_REQUEST = 9 * 60 * 1000; // 5 Min
private static final int ERROR_MSG_ID_TOO_SMALL = 16;
private static final int ERROR_MSG_ID_TOO_BIG = 17;
private static final int ERROR_MSG_ID_BITS = 18;
private static final int ERROR_CONTAINER_MSG_ID_INCORRECT = 19;
private static final int ERROR_TOO_OLD = 20;
private static final int ERROR_SEQ_NO_TOO_SMALL = 32;
private static final int ERROR_SEQ_NO_TOO_BIG = 33;
private static final int ERROR_SEQ_EXPECTED_EVEN = 34;
private static final int ERROR_SEQ_EXPECTED_ODD = 35;
private static final int ERROR_BAD_SERVER_SALT = 48;
private static final int ERROR_BAD_CONTAINER = 64;
private static final int PING_TIMEOUT = 60 * 1000;
private static final int RESEND_TIMEOUT = 60 * 1000;
private static final int FUTURE_REQUEST_COUNT = 64;
private static final int FUTURE_MINIMAL = 5;
private static final long FUTURE_TIMEOUT = 5 * 60 * 60 * 1000;//5 min
private static final long FUTURE_NO_TIME_TIMEOUT = 15 * 60 * 1000;//15 sec
private final String TAG;
private final int INSTANCE_INDEX;
private MTProtoContext protoContext;
private ActorSystem actorSystem;
private byte[] authKey;
private byte[] authKeyId;
private byte[] session;
private AbsMTProtoState state;
private int desiredConnectionCount;
private TransportPool transportPool;
private int mode = MODE_GENERAL;
private final Scheduller scheduller;
private final ArrayList<Long> receivedMessages = new ArrayList<Long>();
private final ActorRef responseActor;
private final ActorRef actionsActor;
private MTProtoCallback callback;
private boolean isClosed;
public MTProto(AbsMTProtoState state,
MTProtoCallback callback,
CallWrapper callWrapper,
int connectionsCount,
int mode) {
this.INSTANCE_INDEX = instanceIndex.incrementAndGet();
this.TAG = "MTProto#" + INSTANCE_INDEX;
this.mode = mode;
this.actorSystem = new ActorSystem();
this.actorSystem.addDispatcher("connector");
this.responseActor = actorSystem.actorOf(processor());
this.actionsActor = actorSystem.actorOf(internal());
this.state = state;
this.callback = callback;
this.authKey = state.getAuthKey();
this.authKeyId = substring(SHA1(authKey), 12, 8);
this.protoContext = MTProtoContext.getInstance();
this.desiredConnectionCount = connectionsCount;
this.session = Entropy.generateSeed(8);
this.scheduller = new Scheduller(this, callWrapper);
this.scheduller.postMessage(new MTPing(Entropy.generateRandomId()), false, Long.MAX_VALUE);
this.transportPool = new TransportTcpPool(this, new TransportPoolCallback() {
@Override
public void onMTMessage(MTMessage message) {
responseActor.send(message);
}
@Override
public void onFastConfirm(int hash) {
// We might not send this to response actor for providing faster confirmation
int[] ids = scheduller.mapFastConfirm(hash);
for (int id : ids) {
MTProto.this.callback.onConfirmed(id);
}
}
}, desiredConnectionCount);
switch (mode) {
case MODE_GENERAL:
case MODE_PUSH:
transportPool.switchMode(TransportPool.MODE_DEFAULT);
break;
case MODE_GENERAL_LOW_MODE:
case MODE_FILE:
transportPool.switchMode(TransportPool.MODE_LOWMODE);
break;
}
this.actionsActor.sendOnce(new RequestPingDelay());
this.actionsActor.sendOnce(new RequestSalt());
}
public AbsMTProtoState getState() {
return state;
}
public void resetNetworkBackoff() {
this.transportPool.resetConnectionBackoff();
this.actionsActor.sendOnce(new RequestPingDelay());
}
public void reloadConnectionInformation() {
this.transportPool.reloadConnectionInformation();
this.actionsActor.sendOnce(new RequestPingDelay());
}
public int getInstanceIndex() {
return INSTANCE_INDEX;
}
public Scheduller getScheduller() {
return scheduller;
}
public byte[] getSession() {
return session;
}
public byte[] getAuthKeyId() {
return authKeyId;
}
public byte[] getAuthKey() {
return authKey;
}
public ActorSystem getActorSystem() {
return actorSystem;
}
public boolean isClosed() {
return isClosed;
}
public int sendRpcMessage(TLMethod request, long timeout, boolean highPriority) {
int id = scheduller.postMessage(request, true, timeout, highPriority);
Logger.d(TAG, "sendMessage #" + id + " " + request.toString() + " with timeout " + timeout + " ms");
return id;
}
public void forgetMessage(int id) {
scheduller.forgetMessage(id);
}
public void switchMode(int mode) {
if (this.mode != mode) {
this.mode = mode;
switch (mode) {
case MODE_GENERAL:
case MODE_PUSH:
transportPool.switchMode(TransportPool.MODE_DEFAULT);
break;
case MODE_GENERAL_LOW_MODE:
case MODE_FILE:
transportPool.switchMode(TransportPool.MODE_LOWMODE);
break;
}
this.actionsActor.sendOnce(new RequestPingDelay());
}
}
public void close() {
if (!isClosed) {
this.isClosed = true;
// TODO: implement
// this.actorSystem.close();
this.transportPool.close();
}
}
// Finding message type
private void onMTMessage(MTMessage mtMessage) {
if (mtMessage.getSeqNo() % 2 == 1) {
scheduller.confirmMessage(mtMessage.getMessageId());
}
if (!needProcessing(mtMessage.getMessageId())) {
if (Logger.LOG_IGNORED) {
Logger.d(TAG, "Ignoring messages #" + mtMessage.getMessageId());
}
return;
}
try {
TLObject intMessage = protoContext.deserializeMessage(new ByteArrayInputStream(mtMessage.getContent()));
onMTProtoMessage(mtMessage.getMessageId(), intMessage);
} catch (DeserializeException e) {
callback.onApiMessage(mtMessage.getContent(), this);
} catch (IOException e) {
Logger.e(TAG, e);
// ???
}
}
private void onMTProtoMessage(long msgId, TLObject object) {
Logger.d(TAG, "MTProtoMessage: " + object.toString());
if (object instanceof MTBadMessage) {
MTBadMessage badMessage = (MTBadMessage) object;
Logger.d(TAG, "BadMessage: " + badMessage.getErrorCode() + " #" + badMessage.getBadMsgId());
scheduller.onMessageConfirmed(badMessage.getBadMsgId());
long time = scheduller.getMessageIdGenerationTime(badMessage.getBadMsgId());
if (time != 0) {
if (badMessage.getErrorCode() == ERROR_MSG_ID_TOO_BIG
|| badMessage.getErrorCode() == ERROR_MSG_ID_TOO_SMALL) {
long delta = System.nanoTime() / 1000000 - time;
TimeOverlord.getInstance().onForcedServerTimeArrived((msgId >> 32) * 1000, delta);
if (badMessage.getErrorCode() == ERROR_MSG_ID_TOO_BIG) {
scheduller.resetMessageId();
}
scheduller.resendAsNewMessage(badMessage.getBadMsgId());
} else if (badMessage.getErrorCode() == ERROR_SEQ_NO_TOO_BIG || badMessage.getErrorCode() == ERROR_SEQ_NO_TOO_SMALL) {
if (scheduller.isMessageFromCurrentGeneration(badMessage.getBadMsgId())) {
Logger.d(TAG, "Resetting session");
session = Entropy.generateSeed(8);
transportPool.onSessionChanged(session);
scheduller.resetSession();
}
scheduller.resendAsNewMessage(badMessage.getBadMsgId());
} else if (badMessage.getErrorCode() == ERROR_BAD_SERVER_SALT) {
long salt = ((MTBadServerSalt) badMessage).getNewSalt();
// Sync time
long delta = System.nanoTime() / 1000000 - time;
TimeOverlord.getInstance().onMethodExecuted(badMessage.getBadMsgId(), msgId, delta);
state.badServerSalt(salt);
Logger.d(TAG, "Reschedule messages because bad_server_salt #" + badMessage.getBadMsgId());
scheduller.resendAsNewMessage(badMessage.getBadMsgId());
this.actionsActor.sendOnce(new RequestSalt());
} else if (badMessage.getErrorCode() == ERROR_BAD_CONTAINER ||
badMessage.getErrorCode() == ERROR_CONTAINER_MSG_ID_INCORRECT) {
scheduller.resendMessage(badMessage.getBadMsgId());
} else if (badMessage.getErrorCode() == ERROR_TOO_OLD) {
scheduller.resendAsNewMessage(badMessage.getBadMsgId());
} else {
if (Logger.LOG_IGNORED) {
Logger.d(TAG, "Ignored BadMsg #" + badMessage.getErrorCode() + " (" + badMessage.getBadMsgId() + ", " + badMessage.getBadMsqSeqno() + ")");
}
scheduller.forgetMessageByMsgId(badMessage.getBadMsgId());
}
} else {
if (Logger.LOG_IGNORED) {
Logger.d(TAG, "Unknown package #" + badMessage.getBadMsgId());
}
}
} else if (object instanceof MTMsgsAck) {
MTMsgsAck ack = (MTMsgsAck) object;
String log = "";
for (Long ackMsgId : ack.getMessages()) {
scheduller.onMessageConfirmed(ackMsgId);
if (log.length() > 0) {
log += ", ";
}
log += ackMsgId;
int id = scheduller.mapSchedullerId(ackMsgId);
if (id > 0) {
callback.onConfirmed(id);
}
}
Logger.d(TAG, "msgs_ack: " + log);
} else if (object instanceof MTRpcResult) {
MTRpcResult result = (MTRpcResult) object;
Logger.d(TAG, "rpc_result: " + result.getMessageId());
int id = scheduller.mapSchedullerId(result.getMessageId());
if (id > 0) {
int responseConstructor = readInt(result.getContent());
if (responseConstructor == MTRpcError.CLASS_ID) {
try {
MTRpcError error = (MTRpcError) protoContext.deserializeMessage(result.getContent());
BytesCache.getInstance().put(result.getContent());
if (error.getErrorCode() == 420) {
if (error.getErrorTag().startsWith("FLOOD_WAIT_")) {
// Secs
int delay = Integer.parseInt(error.getErrorTag().substring("FLOOD_WAIT_".length()));
if (delay <= MAX_INTERNAL_FLOOD_WAIT) {
scheduller.resendAsNewMessageDelayed(result.getMessageId(), delay * 1000);
return;
}
}
}
if (error.getErrorCode() == 401) {
if (error.getErrorTag().equals("AUTH_KEY_UNREGISTERED") ||
error.getErrorTag().equals("AUTH_KEY_INVALID") ||
error.getErrorTag().equals("USER_DEACTIVATED") ||
error.getErrorTag().equals("SESSION_REVOKED") ||
error.getErrorTag().equals("SESSION_EXPIRED")) {
Logger.w(TAG, "Auth key invalidated: " + error.getErrorTag());
callback.onAuthInvalidated(this);
close();
return;
}
}
callback.onRpcError(id, error.getErrorCode(), error.getMessage(), this);
scheduller.forgetMessage(id);
} catch (IOException e) {
Logger.e(TAG, e);
return;
}
} else {
Logger.d(TAG, "rpc_result: " + result.getMessageId() + " #" + Integer.toHexString(responseConstructor));
callback.onRpcResult(id, result.getContent(), this);
BytesCache.getInstance().put(result.getContent());
scheduller.forgetMessage(id);
}
} else {
if (Logger.LOG_IGNORED) {
Logger.d(TAG, "ignored rpc_result: " + result.getMessageId());
}
BytesCache.getInstance().put(result.getContent());
}
scheduller.onMessageConfirmed(result.getMessageId());
long time = scheduller.getMessageIdGenerationTime(result.getMessageId());
if (time != 0) {
long delta = System.nanoTime() / 1000000 - time;
TimeOverlord.getInstance().onMethodExecuted(result.getMessageId(), msgId, delta);
}
} else if (object instanceof MTPong) {
MTPong pong = (MTPong) object;
if (Logger.LOG_PING) {
Logger.d(TAG, "pong: " + pong.getPingId());
}
scheduller.onMessageConfirmed(pong.getMessageId());
scheduller.forgetMessageByMsgId(pong.getMessageId());
long time = scheduller.getMessageIdGenerationTime(pong.getMessageId());
if (time != 0) {
long delta = System.nanoTime() / 1000000 - time;
TimeOverlord.getInstance().onMethodExecuted(pong.getMessageId(), msgId, delta);
}
} else if (object instanceof MTFutureSalts) {
MTFutureSalts salts = (MTFutureSalts) object;
scheduller.onMessageConfirmed(salts.getRequestId());
scheduller.forgetMessageByMsgId(salts.getRequestId());
long time = scheduller.getMessageIdGenerationTime(salts.getRequestId());
if (time > 0) {
KnownSalt[] knownSalts = new KnownSalt[salts.getSalts().size()];
for (int i = 0; i < knownSalts.length; i++) {
MTFutureSalt salt = salts.getSalts().get(i);
knownSalts[i] = new KnownSalt(salt.getValidSince(), salt.getValidUntil(), salt.getSalt());
}
long delta = System.nanoTime() / 1000000 - time;
TimeOverlord.getInstance().onForcedServerTimeArrived(salts.getNow(), delta);
state.mergeKnownSalts(salts.getNow(), knownSalts);
}
} else if (object instanceof MTMessageDetailedInfo) {
MTMessageDetailedInfo detailedInfo = (MTMessageDetailedInfo) object;
Logger.d(TAG, "msg_detailed_info: " + detailedInfo.getMsgId() + ", answer: " + detailedInfo.getAnswerMsgId());
if (receivedMessages.contains(detailedInfo.getAnswerMsgId())) {
scheduller.confirmMessage(detailedInfo.getAnswerMsgId());
} else {
int id = scheduller.mapSchedullerId(detailedInfo.getMsgId());
if (id > 0) {
scheduller.postMessage(new MTNeedResendMessage(new long[]{detailedInfo.getAnswerMsgId()}), false, RESEND_TIMEOUT);
} else {
scheduller.confirmMessage(detailedInfo.getAnswerMsgId());
scheduller.forgetMessageByMsgId(detailedInfo.getMsgId());
}
}
} else if (object instanceof MTNewMessageDetailedInfo) {
MTNewMessageDetailedInfo detailedInfo = (MTNewMessageDetailedInfo) object;
Logger.d(TAG, "msg_new_detailed_info: " + detailedInfo.getAnswerMsgId());
if (receivedMessages.contains(detailedInfo.getAnswerMsgId())) {
scheduller.confirmMessage(detailedInfo.getAnswerMsgId());
} else {
scheduller.postMessage(new MTNeedResendMessage(new long[]{detailedInfo.getAnswerMsgId()}), false, RESEND_TIMEOUT);
}
} else if (object instanceof MTNewSessionCreated) {
callback.onSessionCreated(this);
} else {
if (Logger.LOG_IGNORED) {
Logger.d(TAG, "Ignored MTProto message " + object.toString());
}
}
}
private boolean needProcessing(long messageId) {
synchronized (receivedMessages) {
if (receivedMessages.contains(messageId)) {
return false;
}
if (receivedMessages.size() > MESSAGES_CACHE_MIN) {
boolean isSmallest = true;
for (Long l : receivedMessages) {
if (messageId > l) {
isSmallest = false;
break;
}
}
if (isSmallest) {
return false;
}
}
while (receivedMessages.size() >= MESSAGES_CACHE - 1) {
receivedMessages.remove(0);
}
receivedMessages.add(messageId);
}
return true;
}
private ActorSelection internal() {
return new ActorSelection(Props.create(InternalActionsActor.class, new ActorCreator<InternalActionsActor>() {
@Override
public InternalActionsActor create() {
return new InternalActionsActor(MTProto.this);
}
}), "internal_" + INSTANCE_INDEX);
}
private ActorSelection processor() {
return new ActorSelection(Props.create(ResponseActor.class, new ActorCreator<ResponseActor>() {
@Override
public ResponseActor create() {
return new ResponseActor(MTProto.this);
}
}), "response_" + INSTANCE_INDEX);
}
private static class RequestSalt {
}
private static class RequestPingDelay {
}
private static class InternalActionsActor extends Actor {
private int lastPingMessage = -1;
private final MTProto proto;
public InternalActionsActor(MTProto proto) {
this.proto = proto;
}
@Override
public void onReceive(Object message) {
if (message instanceof RequestSalt) {
onRequestSaltsMessage();
} else if (message instanceof RequestPingDelay) {
onPingDelayMessage();
}
}
public void onRequestSaltsMessage() {
// Logger.d(TAG, "Salt check timeout");
if (TimeOverlord.getInstance().getTimeAccuracy() > 1000) {
// Logger.d(TAG, "Time is not accurate: " + TimeOverlord.getInstance().getTimeAccuracy());
self().send(new RequestSalt(), FUTURE_NO_TIME_TIMEOUT);
return;
}
int count = proto.state.maximumCachedSalts((int) (TimeOverlord.getInstance().getServerTime() / 1000));
if (count < FUTURE_MINIMAL) {
// Logger.d(TAG, "Too few actual salts: " + count + ", requesting news");
proto.scheduller.postMessage(new MTGetFutureSalts(FUTURE_REQUEST_COUNT), false, FUTURE_TIMEOUT);
}
self().send(new RequestSalt(), FUTURE_TIMEOUT);
}
public void onPingDelayMessage() {
if (lastPingMessage >= 0) {
proto.forgetMessage(lastPingMessage);
lastPingMessage = -1;
}
if (proto.mode == MODE_GENERAL) {
// Logger.d(TAG, "Ping delay disconnect for " + PING_INTERVAL + " sec");
lastPingMessage = proto.scheduller.postMessage(new MTPingDelayDisconnect(Entropy.generateRandomId(), PING_INTERVAL),
false, PING_INTERVAL_REQUEST);
self().send(new RequestPingDelay(), PING_INTERVAL_REQUEST);
} else if (proto.mode == MODE_PUSH) {
lastPingMessage = proto.scheduller.postMessage(new MTPing(Entropy.generateRandomId()), false, PING_INTERVAL_REQUEST);
self().send(new RequestPingDelay(), PING_PUSH_REQUEST);
}
}
}
private static class ResponseActor extends Actor {
private final MTProto proto;
private ResponseActor(MTProto proto) {
this.proto = proto;
}
@Override
public void onReceive(Object message) {
if (message instanceof MTMessage) {
MTMessage mtMessage = (MTMessage) message;
proto.onMTMessage(mtMessage);
BytesCache.getInstance().put(mtMessage.getContent());
}
}
}
@Override
public String toString() {
return "mtproto#" + INSTANCE_INDEX;
}
}

View File

@ -1,21 +0,0 @@
package org.telegram.mtproto;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 04.11.13
* Time: 22:11
*/
public interface MTProtoCallback {
public void onSessionCreated(MTProto proto);
public void onAuthInvalidated(MTProto proto);
public void onApiMessage(byte[] message, MTProto proto);
public void onRpcResult(int callId, byte[] response, MTProto proto);
public void onRpcError(int callId, int errorCode, String message, MTProto proto);
public void onConfirmed(int callId);
}

View File

@ -1,26 +0,0 @@
package org.telegram.mtproto;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 6:47
*/
public class ServerException extends IOException {
public ServerException() {
}
public ServerException(String s) {
super(s);
}
public ServerException(String s, Throwable throwable) {
super(s, throwable);
}
public ServerException(Throwable throwable) {
super(throwable);
}
}

View File

@ -1,26 +0,0 @@
package org.telegram.mtproto;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 6:44
*/
public class TransportSecurityException extends IOException {
public TransportSecurityException() {
}
public TransportSecurityException(String s) {
super(s);
}
public TransportSecurityException(String s, Throwable throwable) {
super(s, throwable);
}
public TransportSecurityException(Throwable throwable) {
super(throwable);
}
}

View File

@ -1,64 +0,0 @@
package org.telegram.mtproto.backoff;
import org.telegram.mtproto.log.Logger;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by ex3ndr on 27.12.13.
*/
public class ExponentalBackoff {
private static final int MIN_DELAY = 100;
private static final int MAX_DELAY = 15000;
private static final int MAX_FAILURE_COUNT = 50;
private Random rnd = new Random();
private AtomicInteger currentFailureCount = new AtomicInteger();
private final String TAG;
public ExponentalBackoff(String logTag) {
this.TAG = logTag;
}
public void onFailure() throws InterruptedException {
int val = currentFailureCount.incrementAndGet();
if (val > 50) {
currentFailureCount.compareAndSet(val, MAX_FAILURE_COUNT);
val = MAX_FAILURE_COUNT;
}
int delay = MIN_DELAY + ((MAX_DELAY - MIN_DELAY) / MAX_FAILURE_COUNT) * val;
synchronized (this) {
Logger.d(TAG, "onFailure: wait " + delay + " ms");
wait(delay);
}
}
public void onFailureNoWait() {
Logger.d(TAG, "onFailureNoWait");
int val = currentFailureCount.incrementAndGet();
if (val > 50) {
currentFailureCount.compareAndSet(val, MAX_FAILURE_COUNT);
val = MAX_FAILURE_COUNT;
}
}
public void onSuccess() {
Logger.d(TAG, "onSuccess");
reset();
}
public void reset() {
Logger.d(TAG, "reset");
currentFailureCount.set(0);
synchronized (this) {
notifyAll();
}
}
}

View File

@ -1,15 +0,0 @@
package org.telegram.mtproto.log;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 10.11.13
* Time: 2:11
*/
public interface LogInterface {
void w(String tag, String message);
void d(String tag, String message);
void e(String tag, Throwable t);
}

View File

@ -1,44 +0,0 @@
package org.telegram.mtproto.log;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 3:54
*/
public class Logger {
public static final boolean LOG_THREADS = true;
public static final boolean LOG_IGNORED = true;
public static final boolean LOG_PING = true;
private static LogInterface logInterface;
public static void registerInterface(LogInterface logInterface) {
Logger.logInterface = logInterface;
}
public static void w(String tag, String message) {
if (logInterface != null) {
logInterface.w(tag, message);
} else {
System.out.println(tag + ":" + message);
}
}
public static void d(String tag, String message) {
if (logInterface != null) {
logInterface.d(tag, message);
} else {
System.out.println(tag + ":" + message);
}
}
public static void e(String tag, Throwable t) {
if (logInterface != null) {
logInterface.e(tag, t);
} else {
t.printStackTrace();
}
}
}

View File

@ -1,225 +0,0 @@
package org.telegram.mtproto.pq;
import org.telegram.mtproto.ServerException;
import org.telegram.mtproto.TransportSecurityException;
import org.telegram.mtproto.log.Logger;
import org.telegram.mtproto.secure.CryptoUtils;
import org.telegram.mtproto.secure.Entropy;
import org.telegram.mtproto.secure.Keys;
import org.telegram.mtproto.secure.pq.PQSolver;
import org.telegram.mtproto.state.ConnectionInfo;
import org.telegram.mtproto.time.TimeOverlord;
import org.telegram.mtproto.tl.pq.*;
import org.telegram.mtproto.transport.ConnectionType;
import org.telegram.mtproto.transport.PlainTcpConnection;
import org.telegram.mtproto.transport.TransportRate;
import org.telegram.tl.TLMethod;
import org.telegram.tl.TLObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import static org.telegram.mtproto.secure.CryptoUtils.*;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 4:11
*/
public class Authorizer {
private static final String TAG = "Authorizer";
private static final int AUTH_ATTEMPT_COUNT = 5;
private static final int AUTH_RETRY_COUNT = 5;
private PlainTcpConnection context;
private TLInitContext initContext;
public Authorizer() {
initContext = new TLInitContext();
}
private <T extends TLObject> T executeMethod(TLMethod<T> object) throws IOException {
long requestMessageId = TimeOverlord.getInstance().createWeakMessageId();
long start = System.nanoTime();
byte[] data = object.serialize();
ByteArrayOutputStream out = new ByteArrayOutputStream();
writeLong(0, out); // Empty AUTH_ID
writeLong(requestMessageId, out); // MessageID
writeInt(data.length, out);
writeByteArray(data, out);
byte[] response = context.executeMethod(out.toByteArray());
ByteArrayInputStream in = new ByteArrayInputStream(response);
long authId = readLong(in);
if (authId != 0) {
throw new IOException("Auth id might be equal to zero");
}
long messageId = readLong(in);
// TimeOverlord.getInstance().onMethodExecuted(requestMessageId, messageId, (System.nanoTime() - start) / 1000000);
int length = readInt(in);
byte[] messageResponse = readBytes(length, in);
return object.deserializeResponse(messageResponse, initContext);
}
private PqAuth authAttempt() throws IOException {
// PQ-Auth start
byte[] nonce = Entropy.generateSeed(16);
ResPQ resPQ = executeMethod(new ReqPQ(nonce));
byte[] serverNonce = resPQ.getServerNonce();
long fingerprint = 0;
Keys.Key publicKey = null;
outer:
for (Long srcFingerprint : resPQ.getFingerprints()) {
for (Keys.Key key : Keys.AVAILABLE_KEYS) {
if (srcFingerprint.equals(key.getFingerprint())) {
fingerprint = srcFingerprint;
publicKey = key;
break outer;
}
}
}
if (fingerprint == 0) {
throw new IOException("Unknown public keys");
}
BigInteger pq = loadBigInt(resPQ.getPq());
BigInteger p = null;
try {
long start = System.currentTimeMillis();
p = PQSolver.solvePq(pq);
Logger.d(TAG, "Solved PQ in " + (System.currentTimeMillis() - start) + " ms");
} catch (Exception e) {
throw new IOException();
}
BigInteger q = pq.divide(p);
byte[] newNonce = Entropy.generateSeed(32);
PQInner inner = new PQInner(resPQ.getPq(), fromBigInt(p), fromBigInt(q), nonce,
serverNonce, newNonce);
byte[] pqInner = inner.serialize();
// PQ INNER
byte[] hash = CryptoUtils.SHA1(pqInner);
byte[] seed = Entropy.generateSeed(255 - hash.length - pqInner.length);
byte[] dataWithHash = concat(hash, pqInner, seed);
byte[] encrypted = CryptoUtils.RSA(dataWithHash, publicKey.getPublicKey(), publicKey.getExponent());
long start = System.nanoTime();
ServerDhParams dhParams = executeMethod(new ReqDhParams(nonce, serverNonce, fromBigInt(p), fromBigInt(q),
fingerprint, encrypted));
long dhParamsDuration = (System.nanoTime() - start) / (1000 * 1000);
if (dhParams instanceof ServerDhFailure) {
ServerDhFailure hdFailure = (ServerDhFailure) dhParams;
if (arrayEq(hdFailure.getNewNonceHash(), SHA1(newNonce))) {
throw new ServerException("Received server_DH_params_fail#79cb045d");
} else {
throw new TransportSecurityException("Received server_DH_params_fail#79cb045d with incorrect hash");
}
}
// PQ-Auth end
// DH-Auth start
ServerDhOk serverDhParams = (ServerDhOk) dhParams;
byte[] encryptedAnswer = serverDhParams.getEncryptedAnswer();
byte[] tmpAesKey = concat(SHA1(newNonce, serverNonce), substring(SHA1(serverNonce, newNonce), 0, 12));
byte[] tmpAesIv = concat(concat(substring(SHA1(serverNonce, newNonce), 12, 8), SHA1(newNonce, newNonce)),
substring(newNonce, 0, 4));
byte[] answer = AES256IGEDecrypt(encryptedAnswer, tmpAesIv, tmpAesKey);
ByteArrayInputStream stream = new ByteArrayInputStream(answer);
byte[] answerHash = readBytes(20, stream); // Hash
ServerDhInner dhInner = (ServerDhInner) initContext.deserializeMessage(stream);
if (!arrayEq(answerHash, SHA1(dhInner.serialize()))) {
throw new TransportSecurityException();
}
TimeOverlord.getInstance().onServerTimeArrived(dhInner.getServerTime() * 1000L, dhParamsDuration);
for (int i = 0; i < AUTH_RETRY_COUNT; i++) {
BigInteger b = loadBigInt(Entropy.generateSeed(256));
BigInteger g = new BigInteger(dhInner.getG() + "");
BigInteger dhPrime = loadBigInt(dhInner.getDhPrime());
BigInteger gb = g.modPow(b, dhPrime);
BigInteger authKeyVal = loadBigInt(dhInner.getG_a()).modPow(b, dhPrime);
byte[] authKey = alignKeyZero(fromBigInt(authKeyVal), 256);
byte[] authAuxHash = substring(SHA1(authKey), 0, 8);
ClientDhInner clientDHInner = new ClientDhInner(nonce, serverNonce, i, fromBigInt(gb));
byte[] innerData = clientDHInner.serialize();
byte[] innerDataWithHash = align(concat(SHA1(innerData), innerData), 16);
byte[] dataWithHashEnc = AES256IGEEncrypt(innerDataWithHash, tmpAesIv, tmpAesKey);
DhGenResult result = executeMethod(new ReqSetDhClientParams(nonce, serverNonce, dataWithHashEnc));
if (result instanceof DhGenOk) {
byte[] newNonceHash = substring(SHA1(newNonce, new byte[]{1}, authAuxHash), 4, 16);
if (!arrayEq(result.getNewNonceHash(), newNonceHash))
throw new TransportSecurityException();
long serverSalt = readLong(xor(substring(newNonce, 0, 8), substring(serverNonce, 0, 8)), 0);
return new PqAuth(authKey, serverSalt, context.getSocket());
} else if (result instanceof DhGenRetry) {
byte[] newNonceHash = substring(SHA1(newNonce, new byte[]{2}, authAuxHash), 4, 16);
if (!arrayEq(result.getNewNonceHash(), newNonceHash))
throw new TransportSecurityException();
} else if (result instanceof DhGenFailure) {
byte[] newNonceHash = substring(SHA1(newNonce, new byte[]{3}, authAuxHash), 4, 16);
if (!arrayEq(result.getNewNonceHash(), newNonceHash))
throw new TransportSecurityException();
throw new ServerException();
}
}
throw new ServerException();
}
public PqAuth doAuth(ConnectionInfo[] infos) {
TransportRate rate = new TransportRate(infos);
for (int i = 0; i < AUTH_ATTEMPT_COUNT; i++) {
ConnectionType connectionType = rate.tryConnection();
try {
context = new PlainTcpConnection(connectionType.getHost(), connectionType.getPort());
rate.onConnectionSuccess(connectionType.getId());
} catch (IOException e) {
Logger.e(TAG, e);
rate.onConnectionFailure(connectionType.getId());
continue;
}
try {
return authAttempt();
} catch (IOException e) {
Logger.e(TAG, e);
} finally {
if (context != null) {
context.destroy();
context = null;
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
return null;
}
}
return null;
}
}

View File

@ -1,33 +0,0 @@
package org.telegram.mtproto.pq;
import java.net.Socket;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:14
*/
public class PqAuth {
private byte[] authKey;
private long serverSalt;
private Socket socket;
public PqAuth(byte[] authKey, long serverSalt, Socket socket) {
this.authKey = authKey;
this.serverSalt = serverSalt;
this.socket = socket;
}
public byte[] getAuthKey() {
return authKey;
}
public long getServerSalt() {
return serverSalt;
}
public Socket getSocket() {
return socket;
}
}

View File

@ -1,34 +0,0 @@
package org.telegram.mtproto.schedule;
/**
* Created by ex3ndr on 29.12.13.
*/
public class PrepareSchedule {
private long delay;
private int[] allowedContexts;
private boolean doWait;
public boolean isDoWait() {
return doWait;
}
public void setDoWait(boolean doWait) {
this.doWait = doWait;
}
public long getDelay() {
return delay;
}
public void setDelay(long delay) {
this.delay = delay;
}
public int[] getAllowedContexts() {
return allowedContexts;
}
public void setAllowedContexts(int[] allowedContexts) {
this.allowedContexts = allowedContexts;
}
}

View File

@ -1,37 +0,0 @@
package org.telegram.mtproto.schedule;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 19:59
*/
public class PreparedPackage {
private boolean isHighPriority;
private int seqNo;
private long messageId;
private byte[] content;
public PreparedPackage(int seqNo, long messageId, byte[] content, boolean isHighPriority) {
this.seqNo = seqNo;
this.messageId = messageId;
this.content = content;
this.isHighPriority = isHighPriority;
}
public boolean isHighPriority() {
return isHighPriority;
}
public int getSeqNo() {
return seqNo;
}
public long getMessageId() {
return messageId;
}
public byte[] getContent() {
return content;
}
}

View File

@ -1,627 +0,0 @@
package org.telegram.mtproto.schedule;
import org.telegram.mtproto.CallWrapper;
import org.telegram.mtproto.MTProto;
import org.telegram.mtproto.log.Logger;
import org.telegram.mtproto.time.TimeOverlord;
import org.telegram.mtproto.tl.*;
import org.telegram.tl.TLMethod;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:51
*/
public class Scheduller {
private final String TAG;// = "MTProtoScheduller";
// Share identity values across all connections to avoid collisions
private static final AtomicInteger messagesIds = new AtomicInteger(1);
private static final ConcurrentHashMap<Long, Long> idGenerationTime = new ConcurrentHashMap<Long, Long>();
// private static final int SCHEDULLER_TIMEOUT = 15 * 1000;//15 sec
private static final int SCHEDULLER_TIMEOUT = 24 * 60 * 60 * 1000;//24 hours
private static final long CONFIRM_TIMEOUT = 60 * 1000;//60 sec
private static final int MAX_WORKLOAD_SIZE = 1024;
private static final int BIG_MESSAGE_SIZE = 1024;
private static final long RETRY_TIMEOUT = 15 * 1000;
private static final int MAX_ACK_COUNT = 16;
private SortedMap<Integer, SchedullerPackage> messages = Collections.synchronizedSortedMap(new TreeMap<Integer, SchedullerPackage>());
private HashSet<Long> currentMessageGeneration = new HashSet<Long>();
private HashSet<Long> confirmedMessages = new HashSet<Long>();
private CopyOnWriteArrayList<SchedullerListener> schedullerListeners = new CopyOnWriteArrayList<SchedullerListener>();
private long firstConfirmTime = 0;
private long lastMessageId = 0;
private long lastDependId = 0;
private int seqNo = 0;
private CallWrapper wrapper;
public Scheduller(MTProto mtProto, CallWrapper wrapper) {
TAG = "MTProto#" + mtProto.getInstanceIndex() + "#Scheduller";
this.wrapper = wrapper;
}
public void addListener(SchedullerListener listener) {
schedullerListeners.remove(listener);
schedullerListeners.add(listener);
}
public void removeListener(SchedullerListener listener) {
schedullerListeners.remove(listener);
}
private void notifyChanged() {
for (SchedullerListener listener : schedullerListeners) {
listener.onSchedullerUpdated(this);
}
}
private synchronized long generateMessageId() {
long messageId = TimeOverlord.getInstance().createWeakMessageId();
if (messageId <= lastMessageId) {
messageId = lastMessageId = lastMessageId + 4;
}
while (idGenerationTime.containsKey(messageId)) {
messageId += 4;
}
lastMessageId = messageId;
idGenerationTime.put(messageId, getCurrentTime());
currentMessageGeneration.add(messageId);
return messageId;
}
private synchronized int generateSeqNoWeak() {
return seqNo * 2;
}
private synchronized int generateSeqNo() {
int res = seqNo * 2 + 1;
seqNo++;
return res;
}
private synchronized void generateParams(SchedullerPackage schedullerPackage) {
schedullerPackage.messageId = generateMessageId();
schedullerPackage.seqNo = generateSeqNo();
schedullerPackage.idGenerationTime = getCurrentTime();
schedullerPackage.relatedMessageIds.add(schedullerPackage.messageId);
schedullerPackage.generatedMessageIds.add(schedullerPackage.messageId);
}
private long getCurrentTime() {
return System.nanoTime() / 1000000;
}
public long getMessageIdGenerationTime(long msgId) {
if (idGenerationTime.containsKey(msgId)) {
return idGenerationTime.get(msgId);
}
return 0;
}
public int postMessageDelayed(TLObject object, boolean isRpc, long timeout, int delay, int contextId, boolean highPrioroty) {
int id = messagesIds.incrementAndGet();
SchedullerPackage schedullerPackage = new SchedullerPackage(id);
schedullerPackage.object = object;
schedullerPackage.addTime = getCurrentTime();
schedullerPackage.scheduleTime = schedullerPackage.addTime + delay;
schedullerPackage.expiresTime = schedullerPackage.scheduleTime + timeout;
schedullerPackage.ttlTime = schedullerPackage.scheduleTime + timeout * 2;
schedullerPackage.isRpc = isRpc;
schedullerPackage.queuedToChannel = contextId;
schedullerPackage.priority = highPrioroty ? PRIORITY_HIGH : PRIORITY_NORMAL;
schedullerPackage.isDepend = highPrioroty;
schedullerPackage.supportTag = object.toString();
schedullerPackage.serverErrorCount = 0;
messages.put(id, schedullerPackage);
notifyChanged();
return id;
}
public int postMessage(TLObject object, boolean isApi, long timeout) {
return postMessageDelayed(object, isApi, timeout, 0, -1, false);
}
public int postMessage(TLObject object, boolean isApi, long timeout, boolean highPrioroty) {
return postMessageDelayed(object, isApi, timeout, 0, -1, highPrioroty);
}
public synchronized void prepareScheduller(PrepareSchedule prepareSchedule, int[] connectionIds) {
long time = getCurrentTime();
// Clear packages for unknown channels
outer:
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.queuedToChannel != -1) {
for (int id : connectionIds) {
if (schedullerPackage.queuedToChannel == id) {
continue outer;
}
}
forgetMessage(schedullerPackage.id);
}
}
// If there are no connections provide default delay
if (connectionIds.length == 0) {
prepareSchedule.setDelay(SCHEDULLER_TIMEOUT);
prepareSchedule.setAllowedContexts(connectionIds);
prepareSchedule.setDoWait(true);
return;
}
long minDelay = SCHEDULLER_TIMEOUT;
boolean allConnections = false;
boolean doWait = true;
HashSet<Integer> supportedConnections = new HashSet<Integer>();
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
boolean isPendingPackage = false;
long packageTime = 0;
if (schedullerPackage.state == STATE_QUEUED) {
isPendingPackage = true;
if (schedullerPackage.scheduleTime <= time) {
packageTime = 0;
} else {
packageTime = Math.max(schedullerPackage.scheduleTime - time, 0);
}
} else if (schedullerPackage.state == STATE_SENT) {
if (getCurrentTime() <= schedullerPackage.expiresTime) {
if (time - schedullerPackage.lastAttemptTime >= RETRY_TIMEOUT) {
isPendingPackage = true;
packageTime = 0;
}
}
}
if (isPendingPackage) {
if (schedullerPackage.queuedToChannel == -1) {
allConnections = true;
} else {
supportedConnections.add(schedullerPackage.queuedToChannel);
}
if (packageTime == 0) {
minDelay = 0;
doWait = false;
} else {
minDelay = Math.min(packageTime, minDelay);
}
}
}
prepareSchedule.setDoWait(doWait);
prepareSchedule.setDelay(minDelay);
if (allConnections) {
prepareSchedule.setAllowedContexts(connectionIds);
} else {
Integer[] allowedBoxed = supportedConnections.toArray(new Integer[0]);
int[] allowed = new int[allowedBoxed.length];
for (int i = 0; i < allowed.length; i++) {
allowed[i] = allowedBoxed[i];
}
prepareSchedule.setAllowedContexts(allowed);
}
}
public synchronized void registerFastConfirm(long msgId, int fastConfirm) {
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
boolean contains = false;
for (Long relatedMsgId : schedullerPackage.relatedMessageIds) {
if (relatedMsgId == msgId) {
contains = true;
break;
}
}
if (contains) {
schedullerPackage.relatedFastConfirm.add(fastConfirm);
}
}
}
public int mapSchedullerId(long msgId) {
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.generatedMessageIds.contains(msgId)) {
return schedullerPackage.id;
}
}
return 0;
}
public void resetMessageId() {
lastMessageId = 0;
lastDependId = 0;
}
public void resetSession() {
lastMessageId = 0;
lastDependId = 0;
seqNo = 0;
currentMessageGeneration.clear();
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
schedullerPackage.idGenerationTime = 0;
schedullerPackage.dependMessageId = 0;
schedullerPackage.messageId = 0;
schedullerPackage.seqNo = 0;
}
notifyChanged();
}
public boolean isMessageFromCurrentGeneration(long msgId) {
return currentMessageGeneration.contains(msgId);
}
public void resendAsNewMessage(long msgId) {
resendAsNewMessageDelayed(msgId, 0);
}
public void resendAsNewMessageDelayed(long msgId, int delay) {
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.relatedMessageIds.contains(msgId)) {
schedullerPackage.idGenerationTime = 0;
schedullerPackage.dependMessageId = 0;
schedullerPackage.messageId = 0;
schedullerPackage.seqNo = 0;
schedullerPackage.state = STATE_QUEUED;
schedullerPackage.scheduleTime = getCurrentTime() + delay;
Logger.d(TAG, "Resending as new #" + schedullerPackage.id);
}
}
notifyChanged();
}
public void resendMessage(long msgId) {
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.relatedMessageIds.contains(msgId)) {
// schedullerPackage.relatedMessageIds.clear();
schedullerPackage.state = STATE_QUEUED;
schedullerPackage.lastAttemptTime = 0;
}
}
notifyChanged();
}
public int[] mapFastConfirm(int fastConfirm) {
ArrayList<Integer> res = new ArrayList<Integer>();
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.state == STATE_SENT) {
if (schedullerPackage.relatedFastConfirm.contains(fastConfirm)) {
res.add(schedullerPackage.id);
}
}
}
int[] res2 = new int[res.size()];
for (int i = 0; i < res2.length; i++) {
res2[i] = res.get(i);
}
return res2;
}
public void onMessageConfirmed(long msgId) {
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.state == STATE_SENT) {
if (schedullerPackage.relatedMessageIds.contains(msgId)) {
schedullerPackage.state = STATE_CONFIRMED;
}
}
}
}
public void confirmMessage(long msgId) {
synchronized (confirmedMessages) {
confirmedMessages.add(msgId);
if (firstConfirmTime == 0) {
firstConfirmTime = getCurrentTime();
}
}
}
public synchronized void forgetMessageByMsgId(long msgId) {
int scId = mapSchedullerId(msgId);
if (scId > 0) {
forgetMessage(scId);
}
}
public synchronized void forgetMessage(int id) {
Logger.d(TAG, "Forgetting message: #" + id);
messages.remove(id);
}
private synchronized ArrayList<SchedullerPackage> actualPackages(int contextId) {
ArrayList<SchedullerPackage> foundedPackages = new ArrayList<SchedullerPackage>();
long time = getCurrentTime();
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.queuedToChannel != -1 && contextId != schedullerPackage.queuedToChannel) {
continue;
}
boolean isPendingPackage = false;
if (schedullerPackage.ttlTime <= getCurrentTime()) {
forgetMessage(schedullerPackage.id);
continue;
}
if (schedullerPackage.state == STATE_QUEUED) {
if (schedullerPackage.scheduleTime <= time) {
isPendingPackage = true;
}
} else if (schedullerPackage.state == STATE_SENT) {
if (getCurrentTime() <= schedullerPackage.expiresTime) {
if (getCurrentTime() - schedullerPackage.lastAttemptTime >= RETRY_TIMEOUT) {
isPendingPackage = true;
}
}
}
if (isPendingPackage) {
if (schedullerPackage.serialized == null) {
try {
if (schedullerPackage.isRpc) {
schedullerPackage.serialized = wrapper.wrapObject((TLMethod) schedullerPackage.object).serialize();
} else {
schedullerPackage.serialized = schedullerPackage.object.serialize();
}
} catch (IOException e) {
Logger.e(TAG, e);
forgetMessage(schedullerPackage.id);
continue;
}
}
foundedPackages.add(schedullerPackage);
}
}
return foundedPackages;
}
public synchronized boolean hasRequests() {
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.isRpc)
return true;
}
return false;
}
public synchronized PreparedPackage doSchedule(int contextId, boolean isInited) {
ArrayList<SchedullerPackage> foundedPackages = actualPackages(contextId);
synchronized (confirmedMessages) {
if (foundedPackages.size() == 0 &&
(confirmedMessages.size() <= MAX_ACK_COUNT || (System.nanoTime() - firstConfirmTime) < CONFIRM_TIMEOUT)) {
return null;
}
}
boolean useHighPriority = false;
for (SchedullerPackage p : foundedPackages) {
if (p.priority == PRIORITY_HIGH) {
useHighPriority = true;
break;
}
}
ArrayList<SchedullerPackage> packages = new ArrayList<SchedullerPackage>();
if (useHighPriority) {
Logger.d("Scheduller", "Using high priority scheduling");
int totalSize = 0;
for (SchedullerPackage p : foundedPackages) {
if (p.priority == PRIORITY_HIGH) {
packages.add(p);
totalSize += p.serialized.length;
if (totalSize > MAX_WORKLOAD_SIZE) {
break;
}
}
}
} else {
int totalSize = 0;
for (SchedullerPackage p : foundedPackages) {
packages.add(p);
Logger.d("Scheduller", "Prepare package: " + p.supportTag + " of size " + p.serialized.length);
totalSize += p.serialized.length;
Logger.d("Scheduller", "Total size: " + totalSize);
if (totalSize > MAX_WORKLOAD_SIZE) {
break;
}
}
}
Logger.d(TAG, "Iteration: count: " + packages.size() + ", confirm:" + confirmedMessages.size());
Logger.d(TAG, "Building package");
if (foundedPackages.size() == 0 && confirmedMessages.size() != 0) {
Long[] msgIds;
synchronized (confirmedMessages) {
msgIds = confirmedMessages.toArray(new Long[confirmedMessages.size()]);
confirmedMessages.clear();
}
MTMsgsAck ack = new MTMsgsAck(msgIds);
Logger.d(TAG, "Single msg_ack");
try {
return new PreparedPackage(generateSeqNoWeak(), generateMessageId(), ack.serialize(), useHighPriority);
} catch (IOException e) {
Logger.e(TAG, e);
return null;
}
} else if (foundedPackages.size() == 1 && confirmedMessages.size() == 0) {
SchedullerPackage schedullerPackage = foundedPackages.get(0);
schedullerPackage.state = STATE_SENT;
if (schedullerPackage.idGenerationTime == 0) {
generateParams(schedullerPackage);
}
Logger.d(TAG, "Single package: #" + schedullerPackage.id + " " + schedullerPackage.supportTag + " (" + schedullerPackage.messageId + ", " + schedullerPackage.seqNo + ")");
schedullerPackage.writtenToChannel = contextId;
schedullerPackage.lastAttemptTime = getCurrentTime();
return new PreparedPackage(schedullerPackage.seqNo, schedullerPackage.messageId, schedullerPackage.serialized, useHighPriority);
} else {
MTMessagesContainer container = new MTMessagesContainer();
if ((confirmedMessages.size() > 0 && !useHighPriority) || (!isInited)) {
try {
Long[] msgIds;
synchronized (confirmedMessages) {
msgIds = confirmedMessages.toArray(new Long[0]);
confirmedMessages.clear();
}
MTMsgsAck ack = new MTMsgsAck(msgIds);
Logger.d(TAG, "Adding msg_ack: " + msgIds.length);
container.getMessages().add(new MTMessage(generateMessageId(), generateSeqNoWeak(), ack.serialize()));
} catch (IOException e) {
Logger.e(TAG, e);
}
}
for (SchedullerPackage schedullerPackage : packages) {
schedullerPackage.state = STATE_SENT;
if (schedullerPackage.idGenerationTime == 0) {
generateParams(schedullerPackage);
}
if (schedullerPackage.isDepend) {
if (schedullerPackage.dependMessageId == 0) {
if (lastDependId > 0) {
schedullerPackage.dependMessageId = lastDependId;
} else {
schedullerPackage.dependMessageId = -1;
}
}
lastDependId = schedullerPackage.messageId;
}
schedullerPackage.writtenToChannel = contextId;
schedullerPackage.lastAttemptTime = getCurrentTime();
if (schedullerPackage.isDepend && schedullerPackage.dependMessageId > 0) {
Logger.d(TAG, "Adding package: #" + schedullerPackage.id + " " + schedullerPackage.supportTag + " (" + schedullerPackage.messageId + " on " + schedullerPackage.dependMessageId + ", " + schedullerPackage.seqNo + ")");
MTInvokeAfter invokeAfter = new MTInvokeAfter(schedullerPackage.dependMessageId, schedullerPackage.serialized);
try {
container.getMessages().add(new MTMessage(schedullerPackage.messageId, schedullerPackage.seqNo, invokeAfter.serialize()));
} catch (IOException e) {
Logger.e(TAG, e);
// Never happens
}
} else {
Logger.d(TAG, "Adding package: #" + schedullerPackage.id + " " + schedullerPackage.supportTag + " (" + schedullerPackage.messageId + ", " + schedullerPackage.seqNo + ")");
container.getMessages().add(new MTMessage(schedullerPackage.messageId, schedullerPackage.seqNo, schedullerPackage.serialized));
}
}
long containerMessageId = generateMessageId();
int containerSeq = generateSeqNoWeak();
for (SchedullerPackage schedullerPackage : packages) {
schedullerPackage.relatedMessageIds.add(containerMessageId);
}
Logger.d(TAG, "Sending Package (" + containerMessageId + ", " + containerSeq + ")");
try {
return new PreparedPackage(containerSeq, containerMessageId, container.serialize(), useHighPriority);
} catch (IOException e) {
// Might not happens
Logger.e(TAG, e);
return null;
}
}
}
public synchronized void onConnectionDies(int connectionId) {
Logger.d(TAG, "Connection dies " + connectionId);
for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) {
if (schedullerPackage.writtenToChannel != connectionId) {
continue;
}
if (schedullerPackage.queuedToChannel != -1) {
Logger.d(TAG, "Removing: #" + schedullerPackage.id + " " + schedullerPackage.supportTag);
forgetMessage(schedullerPackage.id);
} else {
if (schedullerPackage.isRpc) {
if (schedullerPackage.state == STATE_CONFIRMED || schedullerPackage.state == STATE_QUEUED) {
Logger.d(TAG, "Re-schedule: #" + schedullerPackage.id + " " + schedullerPackage.supportTag);
schedullerPackage.state = STATE_QUEUED;
schedullerPackage.lastAttemptTime = 0;
}
} else {
if (schedullerPackage.state == STATE_SENT) {
Logger.d(TAG, "Re-schedule: #" + schedullerPackage.id + " " + schedullerPackage.supportTag);
schedullerPackage.state = STATE_QUEUED;
schedullerPackage.lastAttemptTime = 0;
}
}
}
}
notifyChanged();
}
private static final int PRIORITY_HIGH = 1;
private static final int PRIORITY_NORMAL = 0;
private static final int STATE_QUEUED = 0;
private static final int STATE_SENT = 1;
private static final int STATE_CONFIRMED = 2;
private class SchedullerPackage {
public SchedullerPackage(int id) {
this.id = id;
}
public String supportTag;
public int id;
public TLObject object;
public byte[] serialized;
public long addTime;
public long scheduleTime;
public long expiresTime;
public long ttlTime;
public long lastAttemptTime;
public int writtenToChannel = -1;
public int queuedToChannel = -1;
public int state = STATE_QUEUED;
public int priority = PRIORITY_NORMAL;
public boolean isDepend;
public boolean isSent;
public long idGenerationTime;
public long dependMessageId;
public long messageId;
public int seqNo;
public HashSet<Integer> relatedFastConfirm = new HashSet<Integer>();
public HashSet<Long> relatedMessageIds = new HashSet<Long>();
public HashSet<Long> generatedMessageIds = new HashSet<Long>();
public int serverErrorCount;
public boolean isRpc;
}
}

View File

@ -1,8 +0,0 @@
package org.telegram.mtproto.schedule;
/**
* Created by ex3ndr on 03.04.14.
*/
public interface SchedullerListener {
public void onSchedullerUpdated(Scheduller scheduller);
}

View File

@ -1,277 +0,0 @@
package org.telegram.mtproto.secure;
import org.telegram.mtproto.secure.aes.AESImplementation;
import org.telegram.mtproto.secure.aes.DefaultAESImplementation;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
/**
* Author: Korshakov Stepan
* Created: 18.07.13 3:54
*/
public class CryptoUtils {
private static final ThreadLocal<MessageDigest> md5 = new ThreadLocal<MessageDigest>() {
@Override
protected MessageDigest initialValue() {
MessageDigest crypt = null;
try {
crypt = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return crypt;
}
};
private static final ThreadLocal<MessageDigest> sha1 = new ThreadLocal<MessageDigest>() {
@Override
protected MessageDigest initialValue() {
MessageDigest crypt = null;
try {
crypt = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return crypt;
}
};
private static AESImplementation currentImplementation = new DefaultAESImplementation();
public static void setAESImplementation(AESImplementation implementation) {
currentImplementation = implementation;
}
public static byte[] RSA(byte[] src, BigInteger key, BigInteger exponent) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(new RSAPublicKeySpec(key, exponent));
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(src);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
public static void AES256IGEDecryptBig(byte[] src, byte[] dest, int len, byte[] iv, byte[] key) {
currentImplementation.AES256IGEDecrypt(src, dest, len, iv, key);
}
public static byte[] AES256IGEDecrypt(byte[] src, byte[] iv, byte[] key) {
byte[] res = new byte[src.length];
currentImplementation.AES256IGEDecrypt(src, res, src.length, iv, key);
return res;
}
public static void AES256IGEDecrypt(File src, File dest, byte[] iv, byte[] key) throws IOException {
currentImplementation.AES256IGEDecrypt(src.getAbsolutePath(), dest.getAbsolutePath(), iv, key);
}
public static void AES256IGEEncrypt(File src, File dest, byte[] iv, byte[] key) throws IOException {
currentImplementation.AES256IGEEncrypt(src.getAbsolutePath(), dest.getAbsolutePath(), iv, key);
}
public static byte[] AES256IGEEncrypt(byte[] src, byte[] iv, byte[] key) {
byte[] res = new byte[src.length];
currentImplementation.AES256IGEEncrypt(src, res, src.length, iv, key);
return res;
}
public static String MD5(byte[] src) {
try {
MessageDigest crypt = MessageDigest.getInstance("MD5");
crypt.reset();
crypt.update(src);
return ToHex(crypt.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
public static String MD5(RandomAccessFile randomAccessFile) {
try {
MessageDigest crypt = md5.get();
crypt.reset();
byte[] block = new byte[8 * 1024];
for (int i = 0; i < randomAccessFile.length(); i += 8 * 1024) {
int len = (int) Math.min(block.length, randomAccessFile.length() - i);
randomAccessFile.readFully(block, 0, len);
crypt.update(block, 0, len);
}
return ToHex(crypt.digest());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static byte[] MD5Raw(byte[] src) {
MessageDigest crypt = md5.get();
crypt.reset();
crypt.update(src);
return crypt.digest();
}
public static String ToHex(byte[] src) {
String res = "";
for (int i = 0; i < src.length; i++) {
res += String.format("%02X", src[i] & 0xFF);
}
return res.toLowerCase();
}
public static byte[] SHA1(InputStream in) throws IOException {
MessageDigest crypt = sha1.get();
crypt.reset();
// Transfer bytes from in to out
byte[] buf = new byte[4 * 1024];
int len;
while ((len = in.read(buf)) > 0) {
Thread.yield();
// out.write(buf, 0, len);
crypt.update(buf, 0, len);
}
in.close();
return crypt.digest();
}
public static byte[] SHA1(String fileName) throws IOException {
MessageDigest crypt = sha1.get();
crypt.reset();
FileInputStream in = new FileInputStream(fileName);
// Transfer bytes from in to out
byte[] buf = new byte[4 * 1024];
int len;
while ((len = in.read(buf)) > 0) {
Thread.yield();
// out.write(buf, 0, len);
crypt.update(buf, 0, len);
}
in.close();
return crypt.digest();
}
public static byte[] SHA1(byte[] src) {
MessageDigest crypt = sha1.get();
crypt.reset();
crypt.update(src);
return crypt.digest();
}
public static byte[] SHA1(byte[]... src1) {
MessageDigest crypt = sha1.get();
crypt.reset();
for (int i = 0; i < src1.length; i++) {
crypt.update(src1[i]);
}
return crypt.digest();
}
public static boolean arrayEq(byte[] a, byte[] b) {
if (a.length != b.length) {
return false;
}
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i])
return false;
}
return true;
}
public static byte[] concat(byte[]... v) {
int len = 0;
for (int i = 0; i < v.length; i++) {
len += v[i].length;
}
byte[] res = new byte[len];
int offset = 0;
for (int i = 0; i < v.length; i++) {
System.arraycopy(v[i], 0, res, offset, v[i].length);
offset += v[i].length;
}
return res;
}
public static byte[] substring(byte[] src, int start, int len) {
byte[] res = new byte[len];
System.arraycopy(src, start, res, 0, len);
return res;
}
public static byte[] align(byte[] src, int factor) {
if (src.length % factor == 0) {
return src;
}
int padding = factor - src.length % factor;
return concat(src, Entropy.generateSeed(padding));
}
public static byte[] alignKeyZero(byte[] src, int size) {
if (src.length == size) {
return src;
}
if (src.length > size) {
return substring(src, src.length - size, size);
} else {
return concat(new byte[size - src.length], src);
}
}
public static byte[] xor(byte[] a, byte[] b) {
byte[] res = new byte[a.length];
for (int i = 0; i < a.length; i++) {
res[i] = (byte) (a[i] ^ b[i]);
}
return res;
}
public static BigInteger loadBigInt(byte[] data) {
return new BigInteger(1, data);
}
public static byte[] fromBigInt(BigInteger val) {
byte[] res = val.toByteArray();
if (res[0] == 0) {
byte[] res2 = new byte[res.length - 1];
System.arraycopy(res, 1, res2, 0, res2.length);
return res2;
} else {
return res;
}
}
public static boolean isZero(byte[] src) {
for (int i = 0; i < src.length; i++) {
if (src[i] != 0) {
return false;
}
}
return true;
}
}

View File

@ -1,45 +0,0 @@
package org.telegram.mtproto.secure;
import java.security.SecureRandom;
import static org.telegram.mtproto.secure.CryptoUtils.xor;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 4:05
*/
public final class Entropy {
private static SecureRandom random = new SecureRandom();
public static byte[] generateSeed(int size) {
synchronized (random) {
return random.generateSeed(size);
}
}
public static byte[] generateSeed(byte[] sourceSeed) {
synchronized (random) {
return xor(random.generateSeed(sourceSeed.length), sourceSeed);
}
}
public static long generateRandomId() {
synchronized (random) {
return random.nextLong();
}
}
public static int randomInt() {
synchronized (random) {
return random.nextInt();
}
}
public static void feedEntropy(byte[] data) {
synchronized (random) {
random.setSeed(data);
}
}
}

View File

@ -1,19 +0,0 @@
package org.telegram.mtproto.secure;
public class KeyParameter {
private byte[] key;
public KeyParameter(byte[] key) {
this(key, 0, key.length);
}
public KeyParameter(byte[] key, int keyOff, int keyLen) {
this.key = new byte[keyLen];
System.arraycopy(key, keyOff, this.key, 0, keyLen);
}
public byte[] getKey() {
return key;
}
}

View File

@ -1,46 +0,0 @@
package org.telegram.mtproto.secure;
import java.math.BigInteger;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 6:04
*/
public class Keys {
public static class Key {
private BigInteger publicKey;
private BigInteger exponent;
private long fingerprint;
public Key(String publicKey, String exponent, long fingerprint) {
this.publicKey = new BigInteger(publicKey, 16);
this.exponent = new BigInteger(exponent, 16);
this.fingerprint = fingerprint;
}
public BigInteger getPublicKey() {
return publicKey;
}
public BigInteger getExponent() {
return exponent;
}
public long getFingerprint() {
return fingerprint;
}
}
public static final Key[] AVAILABLE_KEYS = new Key[]{
new Key("0c6aeda78b02a251db4b6441031f467fa871faed32526c436524b1fb3b5dca28efb8c089dd1b46d92c895993d87108254951c5f001a0f055f3063dcd14d431a300eb9e29517e359a1c9537e5e87ab1b116faecf5d17546ebc21db234d9d336a693efcb2b6fbcca1e7d1a0be414dca408a11609b9c4269a920b09fed1f9a1597be02761430f09e4bc48fcafbe289054c99dba51b6b5eb7d9c3a2ab4e490545b4676bd620e93804bcac93bf94f73f92c729ca899477ff17625ef14a934d51dc11d5f8650a3364586b3a52fcff2fedec8a8406cac4e751705a472e55707e3c8cd5594342b119c6c3293532d85dbe9271ed54a2fd18b4dc79c04a30951107d5639397",
"010001", 0x9a996a1db11c729bL),
new Key("0b1066749655935f0a5936f517034c943bea7f3365a8931ae52c8bcb14856f004b83d26cf2839be0f22607470d67481771c1ce5ec31de16b20bbaa4ecd2f7d2ecf6b6356f27501c226984263edc046b89fb6d3981546b01d7bd34fedcfcc1058e2d494bda732ff813e50e1c6ae249890b225f82b22b1e55fcb063dc3c0e18e91c28d0c4aa627dec8353eee6038a95a4fd1ca984eb09f94aeb7a2220635a8ceb450ea7e61d915cdb4eecedaa083aa3801daf071855ec1fb38516cb6c2996d2d60c0ecbcfa57e4cf1fb0ed39b2f37e94ab4202ecf595e167b3ca62669a6da520859fb6d6c6203dfdfc79c75ec3ee97da8774b2da903e3435f2cd294670a75a526c1",
"010001", 0xb05b2a6f70cdea78L),
new Key("0c150023e2f70db7985ded064759cfecf0af328e69a41daf4d6f01b538135a6f91f8f8b2a0ec9ba9720ce352efcf6c5680ffc424bd634864902de0b4bd6d49f4e580230e3ae97d95c8b19442b3c0a10d8f5633fecedd6926a7f6dab0ddb7d457f9ea81b8465fcd6fffeed114011df91c059caedaf97625f6c96ecc74725556934ef781d866b34f011fce4d835a090196e9a5f0e4449af7eb697ddb9076494ca5f81104a305b6dd27665722c46b60e5df680fb16b210607ef217652e60236c255f6a28315f4083a96791d7214bf64c1df4fd0db1944fb26a2a57031b32eee64ad15a8ba68885cde74a5bfc920f6abf59ba5c75506373e7130f9042da922179251f",
"010001", 0xc3b42b026ce86b21L),
new Key("0c2a8c55b4a62e2b78a19b91cf692bcdc4ba7c23fe4d06f194e2a0c30f6d9996f7d1a2bcc89bc1ac4333d44359a6c433252d1a8402d9970378b5912b75bc8cc3fa76710a025bcb9032df0b87d7607cc53b928712a174ea2a80a8176623588119d42ffce40205c6d72160860d8d80b22a8b8651907cf388effbef29cd7cf2b4eb8a872052da1351cfe7fec214ce48304ea472bd66329d60115b3420d08f6894b0410b6ab9450249967617670c932f7cbdb5d6fbcce1e492c595f483109999b2661fcdeec31b196429b7834c7211a93c6789d9ee601c18c39e521fda9d7264e61e518add6f0712d2d5228204b851e13c4f322e5c5431c3b7f31089668486aadc59f",
"010001", 0x71e025b6c76033e3L)
};
}

View File

@ -1,833 +0,0 @@
package org.telegram.mtproto.secure.aes;
import org.telegram.mtproto.secure.KeyParameter;
/**
* an implementation of the AES (Rijndael), from FIPS-197.
* <p/>
* For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
* <p/>
* This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
* <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
* <p/>
* There are three levels of tradeoff of speed vs memory
* Because java has no preprocessor, they are written as three separate classes from which to choose
* <p/>
* The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
* and 4 for decryption.
* <p/>
* The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
* adding 12 rotate operations per round to compute the values contained in the other tables from
* the contents of the first
* <p/>
* The slowest version uses no static tables at all and computes the values in each round
* <p/>
* This file contains the fast version with 8Kbytes of static tables for round precomputation
*/
public class AESFastEngine {
// The S box
private static final byte[] S = {
(byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107, (byte) 111, (byte) 197,
(byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171, (byte) 118,
(byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240,
(byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192,
(byte) 183, (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204,
(byte) 52, (byte) 165, (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21,
(byte) 4, (byte) 199, (byte) 35, (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154,
(byte) 7, (byte) 18, (byte) 128, (byte) 226, (byte) 235, (byte) 39, (byte) 178, (byte) 117,
(byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27, (byte) 110, (byte) 90, (byte) 160,
(byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227, (byte) 47, (byte) 132,
(byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177, (byte) 91,
(byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207,
(byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133,
(byte) 69, (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168,
(byte) 81, (byte) 163, (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245,
(byte) 188, (byte) 182, (byte) 218, (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210,
(byte) 205, (byte) 12, (byte) 19, (byte) 236, (byte) 95, (byte) 151, (byte) 68, (byte) 23,
(byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100, (byte) 93, (byte) 25, (byte) 115,
(byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42, (byte) 144, (byte) 136,
(byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11, (byte) 219,
(byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92,
(byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121,
(byte) 231, (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169,
(byte) 108, (byte) 86, (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8,
(byte) 186, (byte) 120, (byte) 37, (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198,
(byte) 232, (byte) 221, (byte) 116, (byte) 31, (byte) 75, (byte) 189, (byte) 139, (byte) 138,
(byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72, (byte) 3, (byte) 246, (byte) 14,
(byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193, (byte) 29, (byte) 158,
(byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142, (byte) 148,
(byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223,
(byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104,
(byte) 65, (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22,
};
// The inverse S-box
private static final byte[] Si = {
(byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165, (byte) 56,
(byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251,
(byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135,
(byte) 52, (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203,
(byte) 84, (byte) 123, (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61,
(byte) 238, (byte) 76, (byte) 149, (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78,
(byte) 8, (byte) 46, (byte) 161, (byte) 102, (byte) 40, (byte) 217, (byte) 36, (byte) 178,
(byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109, (byte) 139, (byte) 209, (byte) 37,
(byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104, (byte) 152, (byte) 22,
(byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182, (byte) 146,
(byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218,
(byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132,
(byte) 144, (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10,
(byte) 247, (byte) 228, (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6,
(byte) 208, (byte) 44, (byte) 30, (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2,
(byte) 193, (byte) 175, (byte) 189, (byte) 3, (byte) 1, (byte) 19, (byte) 138, (byte) 107,
(byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79, (byte) 103, (byte) 220, (byte) 234,
(byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180, (byte) 230, (byte) 115,
(byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53, (byte) 133,
(byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110,
(byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137,
(byte) 111, (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27,
(byte) 252, (byte) 86, (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32,
(byte) 154, (byte) 219, (byte) 192, (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244,
(byte) 31, (byte) 221, (byte) 168, (byte) 51, (byte) 136, (byte) 7, (byte) 199, (byte) 49,
(byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39, (byte) 128, (byte) 236, (byte) 95,
(byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181, (byte) 74, (byte) 13,
(byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156, (byte) 239,
(byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176,
(byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97,
(byte) 23, (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38,
(byte) 225, (byte) 105, (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125,
};
// vector used in calculating key schedule (powers of x in GF(256))
private static final int[] rcon = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91};
// precomputation tables of calculations for rounds
private static final int[] T0 =
{
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
0x3a16162c};
private static final int[] T1 =
{
0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d,
0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203,
0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec,
0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7,
0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae,
0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293,
0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552,
0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f,
0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b,
0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2,
0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761,
0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060,
0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46,
0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8,
0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844,
0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0,
0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030,
0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814,
0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc,
0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0,
0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3,
0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db,
0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e,
0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337,
0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4,
0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e,
0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd,
0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42,
0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701,
0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938,
0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970,
0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592,
0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da,
0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0,
0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6,
0x16162c3a};
private static final int[] T2 =
{
0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2,
0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301,
0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad,
0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4,
0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93,
0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371,
0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7,
0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05,
0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09,
0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e,
0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6,
0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020,
0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb,
0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858,
0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c,
0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040,
0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010,
0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c,
0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44,
0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060,
0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8,
0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49,
0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3,
0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4,
0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c,
0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a,
0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b,
0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e,
0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6,
0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1,
0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9,
0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287,
0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf,
0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099,
0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb,
0x162c3a16};
private static final int[] T3 =
{
0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2,
0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101,
0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad,
0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4,
0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393,
0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171,
0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7,
0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505,
0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909,
0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e,
0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6,
0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020,
0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb,
0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858,
0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c,
0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040,
0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010,
0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c,
0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444,
0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060,
0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8,
0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949,
0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3,
0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4,
0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c,
0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a,
0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b,
0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e,
0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6,
0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1,
0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9,
0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787,
0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf,
0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999,
0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb,
0x2c3a1616};
private static final int[] Tinv0 =
{
0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
0x4257b8d0};
private static final int[] Tinv1 =
{
0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb,
0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6,
0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1,
0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7,
0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3,
0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b,
0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4,
0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0,
0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19,
0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357,
0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b,
0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5,
0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532,
0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51,
0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5,
0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697,
0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738,
0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000,
0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821,
0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2,
0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16,
0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b,
0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c,
0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5,
0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863,
0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d,
0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa,
0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef,
0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf,
0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d,
0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e,
0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3,
0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09,
0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e,
0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0,
0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a,
0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d,
0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8,
0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e,
0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c,
0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735,
0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879,
0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672,
0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de,
0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874,
0x57b8d042};
private static final int[] Tinv2 =
{
0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b,
0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d,
0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0,
0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f,
0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321,
0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e,
0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a,
0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077,
0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd,
0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f,
0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c,
0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be,
0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1,
0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110,
0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d,
0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9,
0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b,
0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000,
0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6,
0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296,
0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a,
0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d,
0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07,
0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b,
0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1,
0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24,
0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48,
0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90,
0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81,
0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92,
0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7,
0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312,
0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978,
0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6,
0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066,
0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98,
0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0,
0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f,
0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41,
0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61,
0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9,
0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce,
0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3,
0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3,
0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c,
0xb8d04257};
private static final int[] Tinv3 =
{
0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab,
0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76,
0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe,
0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f,
0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174,
0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58,
0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace,
0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764,
0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45,
0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f,
0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf,
0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05,
0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a,
0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e,
0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54,
0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd,
0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19,
0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000,
0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c,
0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee,
0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12,
0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09,
0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775,
0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66,
0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4,
0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a,
0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894,
0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033,
0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5,
0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278,
0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739,
0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225,
0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826,
0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff,
0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2,
0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804,
0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef,
0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c,
0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b,
0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7,
0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961,
0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14,
0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d,
0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c,
0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c,
0xd04257b8};
private int shift(
int r,
int shift) {
return (r >>> shift) | (r << -shift);
}
/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
private static final int m1 = 0x80808080;
private static final int m2 = 0x7f7f7f7f;
private static final int m3 = 0x0000001b;
private int FFmulX(int x) {
return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
}
/*
The following defines provide alternative definitions of FFmulX that might
give improved performance if a fast 32-bit multiply is not available.
private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
private static final int m4 = 0x1b1b1b1b;
private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
*/
private int inv_mcol(int x) {
int f2 = FFmulX(x);
int f4 = FFmulX(f2);
int f8 = FFmulX(f4);
int f9 = x ^ f8;
return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
}
private int subWord(int x) {
return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24);
}
/**
* Calculate the necessary round keys
* The number of calculations depends on key size and block size
* AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
* This code is written assuming those are the only possible values
*/
private int[][] generateWorkingKey(
byte[] key,
boolean forEncryption) {
int KC = key.length / 4; // key length in words
int t;
if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) {
throw new IllegalArgumentException("Key length not 128/192/256 bits.");
}
ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block
//
// copy the key into the round key array
//
t = 0;
int i = 0;
while (i < key.length) {
W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16) | (key[i + 3] << 24);
i += 4;
t++;
}
//
// while not enough round key material calculated
// calculate new values
//
int k = (ROUNDS + 1) << 2;
for (i = KC; (i < k); i++) {
int temp = W[(i - 1) >> 2][(i - 1) & 3];
if ((i % KC) == 0) {
temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
} else if ((KC > 6) && ((i % KC) == 4)) {
temp = subWord(temp);
}
W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
}
if (!forEncryption) {
for (int j = 1; j < ROUNDS; j++) {
for (i = 0; i < 4; i++) {
W[j][i] = inv_mcol(W[j][i]);
}
}
}
return W;
}
private int ROUNDS;
private int[][] WorkingKey = null;
private int C0, C1, C2, C3;
private boolean forEncryption;
private static final int BLOCK_SIZE = 16;
/**
* default constructor - 128 bit block size.
*/
public AESFastEngine() {
}
/**
* initialise an AES cipher.
*
* @param forEncryption whether or not we are for encryption.
* @param params the parameters required to set up the cipher.
* @throws IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(
boolean forEncryption,
KeyParameter params) {
WorkingKey = generateWorkingKey(params.getKey(), forEncryption);
this.forEncryption = forEncryption;
}
public String getAlgorithmName() {
return "AES";
}
public int getBlockSize() {
return BLOCK_SIZE;
}
public int processBlock(
byte[] in,
int inOff,
byte[] out,
int outOff) {
if (WorkingKey == null) {
throw new IllegalStateException("AES engine not initialised");
}
if ((inOff + (32 / 2)) > in.length) {
throw new RuntimeException("input buffer too short");
}
if ((outOff + (32 / 2)) > out.length) {
throw new RuntimeException("output buffer too short");
}
if (forEncryption) {
unpackBlock(in, inOff);
encryptBlock(WorkingKey);
packBlock(out, outOff);
} else {
unpackBlock(in, inOff);
decryptBlock(WorkingKey);
packBlock(out, outOff);
}
return BLOCK_SIZE;
}
public void reset() {
}
private void unpackBlock(
byte[] bytes,
int off) {
int index = off;
C0 = (bytes[index++] & 0xff);
C0 |= (bytes[index++] & 0xff) << 8;
C0 |= (bytes[index++] & 0xff) << 16;
C0 |= bytes[index++] << 24;
C1 = (bytes[index++] & 0xff);
C1 |= (bytes[index++] & 0xff) << 8;
C1 |= (bytes[index++] & 0xff) << 16;
C1 |= bytes[index++] << 24;
C2 = (bytes[index++] & 0xff);
C2 |= (bytes[index++] & 0xff) << 8;
C2 |= (bytes[index++] & 0xff) << 16;
C2 |= bytes[index++] << 24;
C3 = (bytes[index++] & 0xff);
C3 |= (bytes[index++] & 0xff) << 8;
C3 |= (bytes[index++] & 0xff) << 16;
C3 |= bytes[index++] << 24;
}
private void packBlock(
byte[] bytes,
int off) {
int index = off;
bytes[index++] = (byte) C0;
bytes[index++] = (byte) (C0 >> 8);
bytes[index++] = (byte) (C0 >> 16);
bytes[index++] = (byte) (C0 >> 24);
bytes[index++] = (byte) C1;
bytes[index++] = (byte) (C1 >> 8);
bytes[index++] = (byte) (C1 >> 16);
bytes[index++] = (byte) (C1 >> 24);
bytes[index++] = (byte) C2;
bytes[index++] = (byte) (C2 >> 8);
bytes[index++] = (byte) (C2 >> 16);
bytes[index++] = (byte) (C2 >> 24);
bytes[index++] = (byte) C3;
bytes[index++] = (byte) (C3 >> 8);
bytes[index++] = (byte) (C3 >> 16);
bytes[index++] = (byte) (C3 >> 24);
}
private void encryptBlock(int[][] KW) {
int r, r0, r1, r2, r3;
C0 ^= KW[0][0];
C1 ^= KW[0][1];
C2 ^= KW[0][2];
C3 ^= KW[0][3];
r = 1;
while (r < ROUNDS - 1) {
r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0];
C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1];
C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2];
C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3];
}
r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
// the final round's table is a simple function of S so we don't use a whole other four tables for it
C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16) ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0];
C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1];
C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16) ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2];
C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16) ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3];
}
private void decryptBlock(int[][] KW) {
int r0, r1, r2, r3;
C0 ^= KW[ROUNDS][0];
C1 ^= KW[ROUNDS][1];
C2 ^= KW[ROUNDS][2];
C3 ^= KW[ROUNDS][3];
int r = ROUNDS - 1;
while (r > 1) {
r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0];
r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1];
r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2];
r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3];
C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255] ^ KW[r][0];
C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255] ^ KW[r][1];
C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255] ^ KW[r][2];
C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255] ^ KW[r--][3];
}
r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0];
r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1];
r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2];
r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r][3];
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16) ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0];
C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16) ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1];
C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16) ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2];
C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16) ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3];
}
}

View File

@ -1,16 +0,0 @@
package org.telegram.mtproto.secure.aes;
import java.io.IOException;
/**
* Created by ex3ndr on 12.02.14.
*/
public abstract interface AESImplementation {
public void AES256IGEDecrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key);
public void AES256IGEEncrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key);
public void AES256IGEEncrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException;
public void AES256IGEDecrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException;
}

View File

@ -1,155 +0,0 @@
package org.telegram.mtproto.secure.aes;
import org.telegram.mtproto.secure.KeyParameter;
import java.io.*;
import static org.telegram.mtproto.secure.CryptoUtils.substring;
/**
* Created by ex3ndr on 12.02.14.
*/
public class DefaultAESImplementation implements AESImplementation {
@Override
public void AES256IGEDecrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key) {
AESFastEngine engine = new AESFastEngine();
engine.init(false, new KeyParameter(key));
int blocksCount = len / 16;
byte[] curIvX = iv;
byte[] curIvY = iv;
int curIvXOffset = 16;
int curIvYOffset = 0;
for (int i = 0; i < blocksCount; i++) {
int offset = i * 16;
for (int j = 0; j < 16; j++) {
dest[offset + j] = (byte) (src[offset + j] ^ curIvX[curIvXOffset + j]);
}
engine.processBlock(dest, offset, dest, offset);
for (int j = 0; j < 16; j++) {
dest[offset + j] = (byte) (dest[offset + j] ^ curIvY[curIvYOffset + j]);
}
curIvY = src;
curIvYOffset = offset;
curIvX = dest;
curIvXOffset = offset;
if (i % 31 == 32) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
@Override
public void AES256IGEEncrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key) {
AESFastEngine engine = new AESFastEngine();
engine.init(true, new KeyParameter(key));
int blocksCount = len / 16;
byte[] curIvX = iv;
byte[] curIvY = iv;
int curIvXOffset = 16;
int curIvYOffset = 0;
for (int i = 0; i < blocksCount; i++) {
int offset = i * 16;
for (int j = 0; j < 16; j++) {
dest[offset + j] = (byte) (src[offset + j] ^ curIvY[curIvYOffset + j]);
}
engine.processBlock(dest, offset, dest, offset);
for (int j = 0; j < 16; j++) {
dest[offset + j] = (byte) (dest[offset + j] ^ curIvX[curIvXOffset + j]);
}
curIvX = src;
curIvXOffset = offset;
curIvY = dest;
curIvYOffset = offset;
}
}
@Override
public void AES256IGEEncrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException {
File src = new File(sourceFile);
File dest = new File(destFile);
AESFastEngine engine = new AESFastEngine();
engine.init(true, new KeyParameter(key));
byte[] curIvX = substring(iv, 16, 16);
byte[] curIvY = substring(iv, 0, 16);
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buffer = new byte[16];
int count;
while ((count = inputStream.read(buffer)) > 0) {
byte[] outData = new byte[16];
for (int j = 0; j < 16; j++) {
outData[j] = (byte) (buffer[j] ^ curIvY[j]);
}
engine.processBlock(outData, 0, outData, 0);
for (int j = 0; j < 16; j++) {
outData[j] = (byte) (outData[j] ^ curIvX[j]);
}
curIvX = buffer;
curIvY = outData;
buffer = new byte[16];
outputStream.write(outData);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
@Override
public void AES256IGEDecrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException {
File src = new File(sourceFile);
File dest = new File(destFile);
AESFastEngine engine = new AESFastEngine();
engine.init(false, new KeyParameter(key));
byte[] curIvX = substring(iv, 16, 16);
byte[] curIvY = substring(iv, 0, 16);
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buffer = new byte[16];
int count;
while ((count = inputStream.read(buffer)) > 0) {
byte[] outData = new byte[16];
for (int j = 0; j < 16; j++) {
outData[j] = (byte) (buffer[j] ^ curIvX[j]);
}
engine.processBlock(outData, 0, outData, 0);
for (int j = 0; j < 16; j++) {
outData[j] = (byte) (outData[j] ^ curIvY[j]);
}
curIvY = buffer;
curIvX = outData;
buffer = new byte[16];
outputStream.write(outData);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
}

View File

@ -1,8 +0,0 @@
package org.telegram.mtproto.secure.pq;
/**
* Created by ex3ndr on 12.02.14.
*/
public interface PQImplementation {
public long findDivider(long src);
}

View File

@ -1,73 +0,0 @@
package org.telegram.mtproto.secure.pq;
import java.util.Random;
/**
* Created by ex3ndr on 12.02.14.
*/
public class PQLopatin implements PQImplementation {
@Override
public long findDivider(long src) {
return findSmallMultiplierLopatin(src);
}
private long GCD(long a, long b) {
while (a != 0 && b != 0) {
while ((b & 1) == 0) {
b >>= 1;
}
while ((a & 1) == 0) {
a >>= 1;
}
if (a > b) {
a -= b;
} else {
b -= a;
}
}
return b == 0 ? a : b;
}
private long findSmallMultiplierLopatin(long what) {
Random r = new Random();
long g = 0;
int it = 0;
for (int i = 0; i < 3; i++) {
int q = (r.nextInt(128) & 15) + 17;
long x = r.nextInt(1000000000) + 1, y = x;
int lim = 1 << (i + 18);
for (int j = 1; j < lim; j++) {
++it;
long a = x, b = x, c = q;
while (b != 0) {
if ((b & 1) != 0) {
c += a;
if (c >= what) {
c -= what;
}
}
a += a;
if (a >= what) {
a -= what;
}
b >>= 1;
}
x = c;
long z = x < y ? y - x : x - y;
g = GCD(z, what);
if (g != 1) {
break;
}
if ((j & (j - 1)) == 0) {
y = x;
}
}
if (g > 1) {
break;
}
}
long p = what / g;
return Math.min(p, g);
}
}

View File

@ -1,22 +0,0 @@
package org.telegram.mtproto.secure.pq;
import java.math.BigInteger;
/**
* Created by ex3ndr on 12.02.14.
*/
public class PQSolver {
private static PQImplementation currentImplementation = new PQLopatin();
public static void setCurrentImplementation(PQImplementation implementation) {
currentImplementation = implementation;
}
public static BigInteger solvePq(BigInteger src) {
return new BigInteger("" + currentImplementation.findDivider(src.longValue()));
}
private PQSolver() {
}
}

View File

@ -1,84 +0,0 @@
package org.telegram.mtproto.state;
import org.telegram.mtproto.time.TimeOverlord;
import java.util.HashMap;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 7:15
*/
public abstract class AbsMTProtoState {
public abstract byte[] getAuthKey();
public abstract ConnectionInfo[] getAvailableConnections();
public abstract KnownSalt[] readKnownSalts();
protected abstract void writeKnownSalts(KnownSalt[] salts);
public void mergeKnownSalts(int currentTime, KnownSalt[] salts) {
KnownSalt[] knownSalts = readKnownSalts();
HashMap<Long, KnownSalt> ids = new HashMap<Long, KnownSalt>();
for (KnownSalt s : knownSalts) {
if (s.getValidUntil() < currentTime) {
continue;
}
ids.put(s.getSalt(), s);
}
for (KnownSalt s : salts) {
if (s.getValidUntil() < currentTime) {
continue;
}
ids.put(s.getSalt(), s);
}
writeKnownSalts(ids.values().toArray(new KnownSalt[0]));
}
public void addCurrentSalt(long salt) {
int time = (int) (TimeOverlord.getInstance().getServerTime() / 1000);
mergeKnownSalts(time, new KnownSalt[]{new KnownSalt(time, time + 30 * 60, salt)});
}
public void badServerSalt(long salt) {
int time = (int) (TimeOverlord.getInstance().getServerTime() / 1000);
writeKnownSalts(new KnownSalt[]{new KnownSalt(time, time + 30 * 60, salt)});
}
public void initialServerSalt(long salt) {
int time = (int) (TimeOverlord.getInstance().getServerTime() / 1000);
writeKnownSalts(new KnownSalt[]{new KnownSalt(time, time + 30 * 60, salt)});
}
public long findActualSalt(int time) {
KnownSalt[] knownSalts = readKnownSalts();
for (KnownSalt salt : knownSalts) {
if (salt.getValidSince() <= time && time <= salt.getValidUntil()) {
return salt.getSalt();
}
}
return 0;
}
public int maximumCachedSalts(int time) {
int count = 0;
for (KnownSalt salt : readKnownSalts()) {
if (salt.getValidSince() > time) {
count++;
}
}
return count;
}
public int maximumCachedSaltsTime() {
int max = 0;
for (KnownSalt salt : readKnownSalts()) {
max = Math.max(max, salt.getValidUntil());
}
return max;
}
}

View File

@ -1,37 +0,0 @@
package org.telegram.mtproto.state;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 7:26
*/
public class ConnectionInfo {
private int id;
private int priority;
private String address;
private int port;
public ConnectionInfo(int id, int priority, String address, int port) {
this.id = id;
this.priority = priority;
this.address = address;
this.port = port;
}
public int getPriority() {
return priority;
}
public String getAddress() {
return address;
}
public int getPort() {
return port;
}
public int getId() {
return id;
}
}

View File

@ -1,31 +0,0 @@
package org.telegram.mtproto.state;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 7:16
*/
public class KnownSalt {
private int validSince;
private int validUntil;
private long salt;
public KnownSalt(int validSince, int validUntil, long salt) {
this.validSince = validSince;
this.validUntil = validUntil;
this.salt = salt;
}
public int getValidSince() {
return validSince;
}
public int getValidUntil() {
return validUntil;
}
public long getSalt() {
return salt;
}
}

View File

@ -1,42 +0,0 @@
package org.telegram.mtproto.state;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 7:21
*/
public class MemoryProtoState extends AbsMTProtoState {
private KnownSalt[] salts = new KnownSalt[0];
private String address;
private int port;
private byte[] authKey;
public MemoryProtoState(byte[] authKey, String address, int port) {
this.authKey = authKey;
this.port = port;
this.address = address;
}
@Override
public byte[] getAuthKey() {
return authKey;
}
@Override
public ConnectionInfo[] getAvailableConnections() {
return new ConnectionInfo[]{new ConnectionInfo(0, 0, address, port)};
}
@Override
public KnownSalt[] readKnownSalts() {
return salts;
}
@Override
protected void writeKnownSalts(KnownSalt[] salts) {
this.salts = salts;
}
}

View File

@ -1,80 +0,0 @@
package org.telegram.mtproto.time;
import org.telegram.mtproto.log.Logger;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 02.11.13
* Time: 21:35
*/
public class TimeOverlord {
private static TimeOverlord instance;
public static synchronized TimeOverlord getInstance() {
if (instance == null) {
instance = new TimeOverlord();
}
return instance;
}
private long nanotimeShift;
private long timeAccuracy = Long.MAX_VALUE;
protected long timeDelta;
private TimeOverlord() {
nanotimeShift = System.currentTimeMillis() - System.nanoTime() / 1000;
}
public long createWeakMessageId() {
return (getServerTime() / 1000) << 32;
}
public long getLocalTime() {
return System.currentTimeMillis();
}
public long getServerTime() {
return getLocalTime() + timeDelta;
}
public long getTimeAccuracy() {
return timeAccuracy;
}
public long getTimeDelta() {
return timeDelta;
}
public void setTimeDelta(long timeDelta, long timeAccuracy) {
this.timeDelta = timeDelta;
this.timeAccuracy = timeAccuracy;
}
public void onForcedServerTimeArrived(long serverTime, long duration) {
timeDelta = serverTime - getLocalTime();
timeAccuracy = duration;
}
public void onServerTimeArrived(long serverTime, long duration) {
if (duration < 0) {
return;
}
if (duration < timeAccuracy) {
timeDelta = serverTime - getLocalTime();
timeAccuracy = duration;
} else if (Math.abs(getLocalTime() - serverTime) > (duration / 2 + timeAccuracy / 2)) {
timeDelta = serverTime - getLocalTime();
timeAccuracy = duration;
}
}
public void onMethodExecuted(long sentId, long responseId, long duration) {
if (duration < 0) {
return;
}
onServerTimeArrived((responseId >> 32) * 1000, duration);
}
}

View File

@ -1,27 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLObject;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 21:40
*/
public abstract class MTBadMessage extends TLObject {
protected long badMsgId;
protected int badMsqSeqno;
protected int errorCode;
public long getBadMsgId() {
return badMsgId;
}
public int getBadMsqSeqno() {
return badMsqSeqno;
}
public int getErrorCode() {
return errorCode;
}
}

View File

@ -1,55 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:47
*/
public class MTBadMessageNotification extends MTBadMessage {
public static final int CLASS_ID = 0xa7eff811;
public MTBadMessageNotification(long badMsgId, int badMsqSeqno, int errorCode) {
this.badMsgId = badMsgId;
this.badMsqSeqno = badMsqSeqno;
this.errorCode = errorCode;
}
public MTBadMessageNotification() {
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(badMsgId, stream);
writeInt(badMsqSeqno, stream);
writeInt(errorCode, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
badMsgId = readLong(stream);
badMsqSeqno = readInt(stream);
errorCode = readInt(stream);
}
@Override
public String toString() {
return "bad_msg_notification#a7eff811";
}
}

View File

@ -1,64 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:45
*/
public class MTBadServerSalt extends MTBadMessage {
public static final int CLASS_ID = 0xedab447b;
private long newSalt;
public MTBadServerSalt(long messageId, int seqNo, int errorNo, long newSalt) {
this.badMsgId = messageId;
this.badMsqSeqno = seqNo;
this.errorCode = errorNo;
this.newSalt = newSalt;
}
public MTBadServerSalt() {
}
public long getNewSalt() {
return newSalt;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(badMsgId, stream);
writeInt(badMsqSeqno, stream);
writeInt(errorCode, stream);
writeLong(newSalt, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
badMsgId = readLong(stream);
badMsqSeqno = readInt(stream);
errorCode = readInt(stream);
newSalt = readLong(stream);
}
@Override
public String toString() {
return "bad_server_salt#edab447b";
}
}

View File

@ -1,71 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 8:00
*/
public class MTFutureSalt extends TLObject {
public static final int CLASS_ID = 0x0949d9dc;
private int validSince;
private int validUntil;
private long salt;
public MTFutureSalt(int validSince, int validUntil, long salt) {
this.validSince = validSince;
this.validUntil = validUntil;
this.salt = salt;
}
public MTFutureSalt() {
}
@Override
public int getClassId() {
return CLASS_ID;
}
public int getValidSince() {
return validSince;
}
public int getValidUntil() {
return validUntil;
}
public long getSalt() {
return salt;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeInt(validSince, stream);
writeInt(validUntil, stream);
writeLong(salt, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
validSince = readInt(stream);
validUntil = readInt(stream);
salt = readLong(stream);
}
@Override
public String toString() {
return "future_salt#0949d9dc";
}
}

View File

@ -1,80 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import org.telegram.tl.TLVector;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 7:58
*/
public class MTFutureSalts extends TLObject {
public static final int CLASS_ID = 0xae500895;
private long requestId;
private int now;
private TLVector<MTFutureSalt> salts = new TLVector<MTFutureSalt>();
public MTFutureSalts(long requestId, int now, TLVector<MTFutureSalt> salts) {
this.requestId = requestId;
this.now = now;
this.salts = salts;
}
public MTFutureSalts() {
}
@Override
public int getClassId() {
return CLASS_ID;
}
public long getRequestId() {
return requestId;
}
public int getNow() {
return now;
}
public TLVector<MTFutureSalt> getSalts() {
return salts;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(requestId, stream);
writeInt(now, stream);
writeInt(salts.size(), stream);
for (MTFutureSalt salt : salts) {
salt.serializeBody(stream);
}
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
requestId = readLong(stream);
now = readInt(stream);
int count = readInt(stream);
salts.clear();
for (int i = 0; i < count; i++) {
MTFutureSalt salt = new MTFutureSalt();
salt.deserializeBody(stream, context);
salts.add(salt);
}
}
@Override
public String toString() {
return "future_salts#ae500895";
}
}

View File

@ -1,52 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.readInt;
import static org.telegram.tl.StreamingUtils.writeInt;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 7:56
*/
public class MTGetFutureSalts extends TLObject {
public static final int CLASS_ID = 0xb921bd04;
private int num;
public MTGetFutureSalts(int num) {
this.num = num;
}
public MTGetFutureSalts() {
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeInt(num, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
num = readInt(stream);
}
@Override
public String toString() {
return "get_future_salts#b921bd04";
}
}

View File

@ -1,50 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created by ex3ndr on 16.12.13.
*/
public class MTInvokeAfter extends TLObject {
public static final int CLASS_ID = 0xcb9f372d;
private long dependMsgId;
private byte[] request;
public MTInvokeAfter(long dependMsgId, byte[] request) {
this.dependMsgId = dependMsgId;
this.request = request;
}
public long getDependMsgId() {
return dependMsgId;
}
public byte[] getRequest() {
return request;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(dependMsgId, stream);
writeByteArray(request, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
throw new UnsupportedOperationException("Unable to deserialize invokeAfterMsg#cb9f372d");
}
}

View File

@ -1,98 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.mtproto.util.BytesCache;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 20:46
*/
public class MTMessage extends TLObject {
private long messageId;
private int seqNo;
private byte[] content;
private int contentLen;
public MTMessage(long messageId, int seqNo, byte[] content) {
this(messageId, seqNo, content, content.length);
}
public MTMessage(long messageId, int seqNo, byte[] content, int contentLen) {
this.messageId = messageId;
this.seqNo = seqNo;
this.content = content;
this.contentLen = contentLen;
}
public MTMessage() {
}
@Override
public int getClassId() {
return 0;
}
public long getMessageId() {
return messageId;
}
public void setMessageId(long messageId) {
this.messageId = messageId;
}
public int getSeqNo() {
return seqNo;
}
public void setSeqNo(int seqNo) {
this.seqNo = seqNo;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public int getContentLen() {
return contentLen;
}
public void setContentLen(int contentLen) {
this.contentLen = contentLen;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(messageId, stream);
writeInt(seqNo, stream);
writeInt(content.length, stream);
writeByteArray(content, 0, contentLen, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
messageId = readLong(stream);
seqNo = readInt(stream);
int size = readInt(stream);
content = BytesCache.getInstance().allocate(size);
readBytes(content, 0, size, stream);
}
@Override
public String toString() {
return "message";
}
}

View File

@ -1,78 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 8:40
*/
public class MTMessageDetailedInfo extends TLObject {
public static final int CLASS_ID = 0x276d3ec6;
private long msgId;
private long answerMsgId;
private int bytes;
private int state;
public MTMessageDetailedInfo(long msgId, long answerMsgId, int bytes, int state) {
this.msgId = msgId;
this.answerMsgId = answerMsgId;
this.bytes = bytes;
this.state = state;
}
public MTMessageDetailedInfo() {
}
public long getMsgId() {
return msgId;
}
public long getAnswerMsgId() {
return answerMsgId;
}
public int getBytes() {
return bytes;
}
public int getState() {
return state;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(msgId, stream);
writeLong(answerMsgId, stream);
writeInt(bytes, stream);
writeInt(state, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
msgId = readLong(stream);
answerMsgId = readLong(stream);
bytes = readInt(stream);
state = readInt(stream);
}
@Override
public String toString() {
return "msg_detailed_info#276d3ec6";
}
}

View File

@ -1,74 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import org.telegram.tl.TLVector;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 20:53
*/
public class MTMessagesContainer extends TLObject {
public static final int CLASS_ID = 0x73f1f8dc;
private TreeSet<MTMessage> messages = new TreeSet<MTMessage>(new Comparator<MTMessage>() {
@Override
public int compare(MTMessage mtMessage, MTMessage mtMessage2) {
return (int) Math.signum(mtMessage.getMessageId() - mtMessage2.getMessageId());
}
});
public MTMessagesContainer(MTMessage[] messages) {
Collections.addAll(this.messages, messages);
}
public MTMessagesContainer() {
}
public TreeSet<MTMessage> getMessages() {
return messages;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeInt(messages.size(), stream);
for (MTMessage message : messages) {
message.serializeBody(stream);
}
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
int size = readInt(stream);
messages.clear();
for (int i = 0; i < size; i++) {
MTMessage message = new MTMessage();
message.deserializeBody(stream, context);
messages.add(message);
}
}
@Override
public String toString() {
return "msg_container#73f1f8dc";
}
}

View File

@ -1,69 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLLongVector;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:30
*/
public class MTMsgsAck extends TLObject {
public static final int CLASS_ID = 0x62d6b459;
private TLLongVector messages;
public MTMsgsAck(TLLongVector messages) {
this.messages = messages;
}
public MTMsgsAck() {
this.messages = new TLLongVector();
}
public MTMsgsAck(long[] msgIds) {
this.messages = new TLLongVector();
for (long id : msgIds) {
this.messages.add(id);
}
}
public MTMsgsAck(Long[] msgIds) {
this.messages = new TLLongVector();
Collections.addAll(this.messages, msgIds);
}
public TLLongVector getMessages() {
return messages;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeTLVector(messages, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
messages = readTLLongVector(stream, context);
}
@Override
public String toString() {
return "msgs_ack#62d6b459";
}
}

View File

@ -1,70 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLLongVector;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import static org.telegram.tl.StreamingUtils.readTLLongVector;
import static org.telegram.tl.StreamingUtils.writeTLVector;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 8:50
*/
public class MTNeedResendMessage extends TLObject {
public static final int CLASS_ID = 0x7d861a08;
private TLLongVector messages;
public MTNeedResendMessage(TLLongVector messages) {
this.messages = messages;
}
public MTNeedResendMessage() {
this.messages = new TLLongVector();
}
public MTNeedResendMessage(long[] msgIds) {
this.messages = new TLLongVector();
for (long id : msgIds) {
this.messages.add(id);
}
}
public MTNeedResendMessage(Long[] msgIds) {
this.messages = new TLLongVector();
Collections.addAll(this.messages, msgIds);
}
public TLLongVector getMessages() {
return messages;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeTLVector(messages, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
messages = readTLLongVector(stream, context);
}
@Override
public String toString() {
return "msg_resend_req#7d861a08";
}
}

View File

@ -1,71 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 07.11.13
* Time: 8:37
*/
public class MTNewMessageDetailedInfo extends TLObject {
public static final int CLASS_ID = 0x809db6df;
private long answerMsgId;
private int bytes;
private int status;
public MTNewMessageDetailedInfo(long answerMsgId, int bytes, int status) {
this.answerMsgId = answerMsgId;
this.bytes = bytes;
this.status = status;
}
public MTNewMessageDetailedInfo() {
}
public long getAnswerMsgId() {
return answerMsgId;
}
public int getBytes() {
return bytes;
}
public int getStatus() {
return status;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(answerMsgId, stream);
writeInt(bytes, stream);
writeInt(status, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
answerMsgId = readLong(stream);
bytes = readInt(stream);
status = readInt(stream);
}
@Override
public String toString() {
return "msg_new_detailed_info#809db6df";
}
}

View File

@ -1,71 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:35
*/
public class MTNewSessionCreated extends TLObject {
public static final int CLASS_ID = 0x9ec20908;
private long firstMsgId;
private byte[] uniqId;
private byte[] serverSalt;
public MTNewSessionCreated(long firstMsgId, byte[] uniqId, byte[] serverSalt) {
this.firstMsgId = firstMsgId;
this.uniqId = uniqId;
this.serverSalt = serverSalt;
}
public MTNewSessionCreated() {
}
public long getFirstMsgId() {
return firstMsgId;
}
public byte[] getUniqId() {
return uniqId;
}
public byte[] getServerSalt() {
return serverSalt;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(firstMsgId, stream);
writeByteArray(uniqId, stream);
writeByteArray(serverSalt, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
firstMsgId = readLong(stream);
uniqId = readBytes(8, stream);
serverSalt = readBytes(8, stream);
}
@Override
public String toString() {
return "new_session_created#9ec20908";
}
}

View File

@ -1,50 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:22
*/
public class MTPing extends TLObject {
public static final int CLASS_ID = 0x7abe77ec;
private long pingId;
public MTPing(long pingId) {
this.pingId = pingId;
}
public MTPing() {
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(pingId, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
pingId = readLong(stream);
}
@Override
public String toString() {
return "ping#7abe77ec";
}
}

View File

@ -1,62 +0,0 @@
package org.telegram.mtproto.tl;
import org.telegram.tl.TLContext;
import org.telegram.tl.TLObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.telegram.tl.StreamingUtils.*;
/**
* Created with IntelliJ IDEA.
* User: ex3ndr
* Date: 03.11.13
* Time: 8:22
*/
public class MTPingDelayDisconnect extends TLObject {
public static final int CLASS_ID = 0xf3427b8c;
private long pingId;
private int disconnectDelay;
public MTPingDelayDisconnect(long pingId, int disconnectDelay) {
this.pingId = pingId;
this.disconnectDelay = disconnectDelay;
}
public MTPingDelayDisconnect() {
}
public long getPingId() {
return pingId;
}
public int getDisconnectDelay() {
return disconnectDelay;
}
@Override
public int getClassId() {
return CLASS_ID;
}
@Override
public void serializeBody(OutputStream stream) throws IOException {
writeLong(pingId, stream);
writeInt(disconnectDelay, stream);
}
@Override
public void deserializeBody(InputStream stream, TLContext context) throws IOException {
pingId = readLong(stream);
disconnectDelay = readInt(stream);
}
@Override
public String toString() {
return "ping_delay_disconnect#f3427b8c";
}
}

Some files were not shown because too many files have changed in this diff Show More