Author: mturk
Date: Wed Aug 17 12:37:43 2011
New Revision: 1158660

URL: http://svn.apache.org/viewvc?rev=1158660&view=rev
Log:
Add direct native pointer methods

Modified:
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/Stream.java
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Deflater.java
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Inflater.java
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2.java
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Deflater.java
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Inflater.java

Modified: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/Stream.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/Stream.java?rev=1158660&r1=1158659&r2=1158660&view=diff
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/Stream.java
 (original)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/Stream.java
 Wed Aug 17 12:37:43 2011
@@ -196,6 +196,8 @@ public abstract class Stream
             throw new OperationNotSupportedException();
         if (off + count > pointer.sizeof())
             throw new IndexOutOfBoundsException();
+        if (pointer.isNull())
+            throw new NullPointerException();
         return read(pointer.address() + off, count);
     }
 
@@ -234,6 +236,8 @@ public abstract class Stream
             throw new OperationNotSupportedException();
         if (off + count > pointer.sizeof())
             throw new IndexOutOfBoundsException();
+        if (pointer.isNull())
+            throw new NullPointerException();
         return write(pointer.address() + off, count);
     }
 

Modified: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Deflater.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Deflater.java?rev=1158660&r1=1158659&r2=1158660&view=diff
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Deflater.java
 (original)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Deflater.java
 Wed Aug 17 12:37:43 2011
@@ -119,7 +119,7 @@ public abstract class Deflater implement
      */
     public abstract void flush()
         throws IllegalStateException;
-        
+
     /**
      * Sets the input buffer the {@code Deflater} will use to extract
      * uncompressed bytes for later compression.
@@ -148,8 +148,6 @@ public abstract class Deflater implement
     public void setInput(byte[] b)
         throws InvalidArgumentException
     {
-        if (b == null)
-            throw new NullPointerException();
         if (b.length < 1)
             throw new InvalidArgumentException();
         setInput(b, 0, b.length);
@@ -172,12 +170,33 @@ public abstract class Deflater implement
      * returns {@code true} indicating that more input data is required.
      *
      * @param b pointer to the input data bytes.
-     * @param off the start offset of the input data
      * @param len the length of the input data
      * @throws InvalidArgumentException if the provided parameters are invalid
      */
-    public abstract void setInput(Pointer b, long off, int len)
+    public abstract void setInput(long b, int len)
         throws InvalidArgumentException;
+        
+    /**
+     * Set input data for compression.
+     * This should be called whenever the {@code needsInput()}
+     * returns {@code true} indicating that more input data is required.
+     *
+     * @param b pointer to the input data bytes.
+     * @param off the start offset of the input data
+     * @param len the length of the input data
+     * @throws InvalidArgumentException if the provided parameters are invalid
+     */
+    public void setInput(Pointer b, long off, int len)
+        throws InvalidArgumentException
+    {
+        if (b.isNull())
+            throw new NullPointerException();
+        if (len < 1)
+            throw new InvalidArgumentException();
+        if (off < 0L || off + len > b.sizeof())
+            throw new IndexOutOfBoundsException();
+        setInput(b.address() + off, len);
+    }
 
     /**
      * Set input data for compression.
@@ -190,10 +209,12 @@ public abstract class Deflater implement
     public void setInput(Pointer b)
         throws InvalidArgumentException
     {
+        if (b.isNull())
+            throw new NullPointerException();
         int len = Utils.toInteger(b.sizeof());
         if (len < 1)
             throw new InvalidArgumentException();
-        setInput(b, 0L, len);
+        setInput(b.address(), len);
     }
 
     /**
@@ -301,7 +322,7 @@ public abstract class Deflater implement
      * @throws InvalidDataException if the input data was invalid or corrupt.
      * @throws OutOfMemoryError if the memory allocation failed
      */
-    public abstract int deflate(Pointer b, long off, int len)
+    public abstract int deflate(long b, int len)
         throws InvalidArgumentException,
                InvalidDataException,
                OutOfMemoryError;
@@ -313,6 +334,32 @@ public abstract class Deflater implement
      * be called in order to determine if more input data is required.
      *
      * @return the actual number of bytes of compressed data or {@code -1}
+     *          if end of compressed data has been reached.
+     * @throws InvalidArgumentException if the provided parameters are invalid
+     * @throws InvalidDataException if the input data was invalid or corrupt.
+     * @throws OutOfMemoryError if the memory allocation failed
+     */
+    public int deflate(Pointer b, long off, int len)
+        throws InvalidArgumentException,
+               InvalidDataException,
+               OutOfMemoryError
+    {
+        if (b.isNull())
+            throw new NullPointerException();
+        if (len < 1)
+            throw new InvalidArgumentException();
+        if (off < 0L || off + len > b.sizeof())
+            throw new IndexOutOfBoundsException();
+        return deflate(b.address() + off, len);
+    }
+
+    /**
+     * Compressees the data (previously passed to {@code setInput})
+     * into the supplied buffer.
+     * A return value of {@code 0} indicates that {@code needsInput()} should
+     * be called in order to determine if more input data is required.
+     *
+     * @return the actual number of bytes of compressed data or {@code -1}
      *          if end of uncompressed data has been reached.
      * @throws InvalidArgumentException if the provided parameters are invalid
      * @throws InvalidDataException if the provided data was invalid
@@ -323,12 +370,12 @@ public abstract class Deflater implement
                InvalidDataException,
                OutOfMemoryError
     {
-        if (b == null)
+        if (b.isNull())
             throw new NullPointerException();
         int len = Utils.toInteger(b.sizeof());
         if (len < 1)
             throw new InvalidArgumentException();
-        return deflate(b, 0, len);
+        return deflate(b.address(), len);
     }
                
 }

Modified: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Inflater.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Inflater.java?rev=1158660&r1=1158659&r2=1158660&view=diff
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Inflater.java
 (original)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Inflater.java
 Wed Aug 17 12:37:43 2011
@@ -93,7 +93,7 @@ public abstract class Inflater implement
      *          been reached.
      */
     public abstract boolean finished();
-
+    
     /**
      * Set input data for decompression.
      * This should be called whenever the {@code needsInput()}
@@ -143,12 +143,11 @@ public abstract class Inflater implement
      * returns {@code true} indicating that more input data is required.
      *
      * @param b pointer to the input data bytes.
-     * @param off the start offset of the input data
      * @param len the length of the input data
      *
      * @throws InvalidArgumentException if the provided parameters are invalid
      */
-    public abstract void setInput(Pointer b, long off, int len)
+    public abstract void setInput(long b, int len)
         throws InvalidArgumentException;
 
     /**
@@ -157,16 +156,40 @@ public abstract class Inflater implement
      * returns {@code true} indicating that more input data is required.
      *
      * @param b pointer to the input data bytes.
+     * @param off the start offset of the input data
+     * @param len the length of the input data
+     *
+     * @throws InvalidArgumentException if the provided parameters are invalid
+     */
+    public void setInput(Pointer b, long off, int len)
+        throws InvalidArgumentException
+    {
+        if (b.isNull())
+            throw new NullPointerException();
+        if (len < 1)
+            throw new InvalidArgumentException();
+        setInput(b.address() + off, len);
+        
+    }
+
+    /**
+     * Set input data for decompression.
+     * This should be called whenever the {@code needsInput()}
+     * returns {@code true} indicating that more input data is required.
+     *
+     * @param b pointer to the input data bytes.
      *
      * @throws InvalidArgumentException if the provided parameters are invalid
      */
     public void setInput(Pointer b)
         throws InvalidArgumentException
     {
+        if (b.isNull())
+            throw new NullPointerException();
         int len = Utils.toInteger(b.sizeof());
         if (len < 1)
             throw new InvalidArgumentException();
-        setInput(b, 0L, len);
+        setInput(b.address(), len);
     }
 
     /**
@@ -273,12 +296,11 @@ public abstract class Inflater implement
 
     /**
      * Uncompresses up to {@code len} bytes from current input and stores
-     * them in {@code buf} starting at {@code off}.
+     * them in {@code b}.
      * A return value of {@code 0} indicates that {@code needsInput()} should
      * be called in order to determine if more input data is required.
      *
      * @param b the buffer to write decompressed bytes to.
-     * @param off the offset in buffer where to start writing decompressed 
data.
      * @param len the number of inflated bytes to write to {@code b}.
      *
      * @return the actual number of bytes of uncompressed data or {@code -1}
@@ -288,12 +310,42 @@ public abstract class Inflater implement
      * @throws InvalidDataException if the input data was invalid or corrupt.
      * @throws OutOfMemoryError if the memory allocation failed
      */
-    public abstract int inflate(Pointer b, long off, int len)
+    public abstract int inflate(long b, int len)
         throws InvalidArgumentException,
                InvalidDataException,
                OutOfMemoryError;
 
     /**
+     * Uncompresses up to {@code len} bytes from current input and stores
+     * them in {@code buf} starting at {@code off}.
+     * A return value of {@code 0} indicates that {@code needsInput()} should
+     * be called in order to determine if more input data is required.
+     *
+     * @param b the buffer to write decompressed bytes to.
+     * @param off the offset in buffer where to start writing decompressed 
data.
+     * @param len the number of inflated bytes to write to {@code b}.
+     *
+     * @return the actual number of bytes of uncompressed data or {@code -1}
+     *          if end of compressed data has been reached.
+     *
+     * @throws InvalidArgumentException if the provided parameters are invalid
+     * @throws InvalidDataException if the input data was invalid or corrupt.
+     * @throws OutOfMemoryError if the memory allocation failed
+     */
+    public int inflate(Pointer b, long off, int len)
+        throws InvalidArgumentException,
+               InvalidDataException,
+               OutOfMemoryError
+    {
+        if (b.isNull())
+            throw new NullPointerException();
+        if (len < 1)
+            throw new InvalidArgumentException();
+        if (off < 0L || off + len > b.sizeof())
+            throw new IndexOutOfBoundsException();
+        return inflate(b.address() + off, len);
+    }
+    /**
      * Uncompresses bytes from current input and stores them in
      * the {@code b}.
      * A return value of {@code 0} indicates that {@code needsInput()} should
@@ -313,10 +365,12 @@ public abstract class Inflater implement
                InvalidDataException,
                OutOfMemoryError
     {
+        if (b.isNull())
+            throw new NullPointerException();
         int len = Utils.toInteger(b.sizeof());
         if (len < 1)
             throw new InvalidArgumentException();
-        return inflate(b, 0L, len);
+        return inflate(b.address(), len);
     }
                
 }

Modified: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2.java?rev=1158660&r1=1158659&r2=1158660&view=diff
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2.java
 (original)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2.java
 Wed Aug 17 12:37:43 2011
@@ -251,25 +251,70 @@ public final class Bzip2
      * @throws OverflowException if the size of the compressed data exceeds
      *          the size of the destination buffer.
      */
-    public static long buffToBuffCompress(Pointer src,
-                                   long srcPos,
-                                   Pointer dst,
-                                   long dstPos,
-                                   int length,
-                                   int blockSize100k,
-                                   int workFactor)
+    public static long buffToBuffCompress(long src, long dst,
+                                          int dstLen,
+                                          int length,
+                                          int blockSize100k,
+                                          int workFactor)
         throws InvalidArgumentException,
                InvalidRangeException,
                OverflowException
     {
         if (blockSize100k < 1 || blockSize100k > 9 || workFactor < 0 || 
workFactor > 250)
             throw new InvalidArgumentException();
+        if (src == 0L || dst == 0L)
+            throw new NullPointerException();
+        if (length < 1 || dstLen < 1)
+            throw new IndexOutOfBoundsException();
+        return deflate2(src, dst, dstLen, length, blockSize100k, workFactor);
+    }
+
+    /**
+     * Compress data buffer.
+     * <p>
+     * Compression in this manner is a one-shot event, done with a single call
+     * to this function. The resulting compressed data is a complete bzip2
+     * format data stream. There is no mechanism for making additional calls
+     * to provide extra input data. If you want that kind of mechanism, use
+     * the low-level interface.
+     * </p>
+     *
+     * @param src the source data buffer.
+     * @param srcPos starting position in the source buffer.
+     * @param dst destination buffer that will hold compressed data.
+     * @param dstPos stating position in the destination buffer.
+     * @param length number of bytes to compress.
+     * @param blockSize100k the block size to be used for compression. It
+     *          should be a value between {@code 1} and {@code 9} inclusive.
+     * @param workFactor controls how the compression phase behaves when
+     *          presented with worst case. Allowable values range from
+     *          {@code 0} to {@code 250} inclusive.
+     * @return the size of the compressed data.
+     *
+     * @throws InvalidArgumentException if any of the argumets is invalid.
+     * @throws OverflowException if the size of the compressed data exceeds
+     *          the size of the destination buffer.
+     */
+    public static long buffToBuffCompress(Pointer src,
+                                          long srcPos,
+                                          Pointer dst,
+                                          long dstPos,
+                                          int length,
+                                          int blockSize100k,
+                                          int workFactor)
+        throws InvalidArgumentException,
+               InvalidRangeException,
+               OverflowException
+    {
+        if (src.isNull() || dst.isNull())
+            throw new NullPointerException();
+        if (srcPos < 0L || dstPos < 0L)
+            throw new IndexOutOfBoundsException();
         long srca = src.address() + srcPos;
         long dsta = dst.address() + dstPos;
         int  dlen = Utils.toInteger(dst.sizeof()  - dstPos);
-        if (dlen < 1 || srcPos < 0L || dstPos < 0L)
-            throw new IndexOutOfBoundsException();
-        return deflate2(srca, dsta, (int)dlen, length, blockSize100k, 
workFactor);
+        return buffToBuffCompress(srca, dsta, dlen, length,
+                                  blockSize100k, workFactor);
     }
 
     /**
@@ -299,11 +344,11 @@ public final class Bzip2
      *          the size of the destination buffer.
      */
     public static int buffToBuffDecompress(byte[] src,
-                                    int srcPos,
-                                    byte[] dest,
-                                    int destPos,
-                                    int length,
-                                    boolean small)
+                                           int srcPos,
+                                           byte[] dest,
+                                           int destPos,
+                                           int length,
+                                           boolean small)
         throws InvalidArgumentException,
                InvalidDataException,
                OverflowException
@@ -364,6 +409,47 @@ public final class Bzip2
      * </p>
      *
      * @param src the source data buffer.
+     * @param dst destination buffer that will hold uncompressed data.
+     * @param dstLen length of the destination buffer.
+     * @param length number of bytes to uncompress.
+     * @param small if {@code true} the library will use an alternative
+     *          decompression algorithm which uses less memory but at the cost
+     *          of decompressing more slowly.
+     * @return the size of the uncompressed data.
+     *
+     * @throws InvalidArgumentException if any of the argumets is invalid.
+     * @throws InvalidDataException if provided data is corrupt or contains
+     *          invalid bzip2 data.
+     * @throws OverflowException if the size of the uncompressed data exceeds
+     *          the size of the destination buffer.
+     */
+    public static long buffToBuffDecompress(long src,
+                                            long dst,
+                                            int  dstLen,
+                                            int  length,
+                                            boolean small)
+        throws InvalidArgumentException,
+               InvalidDataException,
+               OverflowException
+    {
+        if (src == 0L || dst == 0L)
+            throw new NullPointerException();
+        if (length < 1 || dstLen < 1)
+            throw new IndexOutOfBoundsException();
+        return inflate2(src, dst, dstLen, length, small);
+    }
+
+    /**
+     * Decompress data buffer.
+     * <p>
+     * Because the compression ratio of the compressed data cannot be known
+     * in advance, there is no easy way to guarantee that the output buffer
+     * will be big enough. You may of course make arrangements in your code
+     * to record the size of the uncompressed data, but such a mechanism is
+     * beyond the scope of this library.
+     * </p>
+     *
+     * @param src the source data buffer.
      * @param srcPos starting position in the source buffer.
      * @param dst destination buffer that will hold uncompressed data.
      * @param dstPos stating position in the destination buffer.
@@ -380,23 +466,23 @@ public final class Bzip2
      *          the size of the destination buffer.
      */
     public static long buffToBuffDecompress(Pointer src,
-                                     long srcPos,
-                                     Pointer dst,
-                                     long dstPos,
-                                     int length,
-                                     boolean small)
+                                            long srcPos,
+                                            Pointer dst,
+                                            long dstPos,
+                                            int length,
+                                            boolean small)
         throws InvalidArgumentException,
                InvalidDataException,
                OverflowException
     {
+        if (src.isNull() || dst.isNull())
+            throw new NullPointerException();
+        if (srcPos < 0L || dstPos < 0L)
+            throw new IndexOutOfBoundsException();
         long srca = src.address() + srcPos;
         long dsta = dst.address() + dstPos;
         int  dlen = Utils.toInteger(dst.sizeof()  - dstPos);
-        if (dlen < 1 || srcPos < 0L || dstPos < 0L)
-            throw new IndexOutOfBoundsException();
-        /* TODO: Check for param validity */
-        return inflate2(srca, dsta, dlen, length, small);
+        return buffToBuffDecompress(srca, dsta, dlen, length, small);         
     }
 
 }
-

Modified: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Deflater.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Deflater.java?rev=1158660&r1=1158659&r2=1158660&view=diff
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Deflater.java
 (original)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Deflater.java
 Wed Aug 17 12:37:43 2011
@@ -157,16 +157,14 @@ public class Bzip2Deflater extends Defla
     }
 
     @Override
-    public synchronized void setInput(Pointer b, long off, int len)
+    public synchronized void setInput(long b, int len)
         throws InvalidArgumentException
     {
         if (len < 1)
             throw new InvalidArgumentException();
-        if (off < 0L || off + len > b.sizeof())
-            throw new IndexOutOfBoundsException();
         if (handle == 0L)
             throw new IllegalStateException();
-        Bzip2Impl.setInput2(handle, b.address() + off, len);
+        Bzip2Impl.setInput2(handle, b, len);
     }
 
     @Override
@@ -243,18 +241,18 @@ public class Bzip2Deflater extends Defla
     }
 
     @Override
-    public int deflate(Pointer b, long off, int len)
+    public int deflate(long b, int len)
         throws InvalidArgumentException,
                InvalidDataException,
                OutOfMemoryError
     {
+        if (b == 0L)
+            throw new NullPointerException();
         if (len < 1)
             throw new InvalidArgumentException();
-        if (off < 0L || off + len > b.sizeof())
-            throw new IndexOutOfBoundsException();
         if (handle == 0L)
             throw new IllegalStateException();
-        return deflate3(handle, b.address() + off, len);
+        return deflate3(handle, b, len);
     }
 
 }

Modified: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Inflater.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Inflater.java?rev=1158660&r1=1158659&r2=1158660&view=diff
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Inflater.java
 (original)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/bzip2/Bzip2Inflater.java
 Wed Aug 17 12:37:43 2011
@@ -108,12 +108,16 @@ public final class Bzip2Inflater extends
     @Override
     public synchronized boolean needsInput()
     {
+        if (handle == 0L)
+            throw new IllegalStateException();
         return Bzip2Impl.needsInput(handle);
     }
 
     @Override
     public synchronized boolean finished()
     {
+        if (handle == 0L)
+            throw new IllegalStateException();
         return Bzip2Impl.finished(handle);
     }
 
@@ -125,6 +129,8 @@ public final class Bzip2Inflater extends
             throw new InvalidArgumentException();
         if (off < 0 || off + len > b.length)
             throw new ArrayIndexOutOfBoundsException();
+        if (handle == 0L)
+            throw new IllegalStateException();
         Bzip2Impl.setInput0(handle, b, off, len);
     }
 
@@ -135,29 +141,37 @@ public final class Bzip2Inflater extends
         int len = b.remaining();
         if (len < 1)
             throw new InvalidArgumentException();
+        if (handle == 0L)
+            throw new IllegalStateException();
         Bzip2Impl.setInput1(handle, b, b.position(), len);
     }
 
     @Override
-    public synchronized void setInput(Pointer b, long off, int len)
+    public synchronized void setInput(long b, int len)
         throws InvalidArgumentException
     {
         if (len < 1)
             throw new InvalidArgumentException();
-        if (off < 0L || off + len > b.sizeof())
-            throw new IndexOutOfBoundsException();
-        Bzip2Impl.setInput2(handle, b.address() + off, len);
+        if (b == 0L)
+            throw new NullPointerException();
+        if (handle == 0L)
+            throw new IllegalStateException();
+        Bzip2Impl.setInput2(handle, b, len);
     }
 
     @Override
     public synchronized long getTotalIn()
     {
+        if (handle == 0L)
+            throw new IllegalStateException();
         return Bzip2Impl.getTotalIn(handle);
     }
 
     @Override
     public synchronized long getTotalOut()
     {
+        if (handle == 0L)
+            throw new IllegalStateException();
         return Bzip2Impl.getTotalOut(handle);
     }
 
@@ -199,18 +213,18 @@ public final class Bzip2Inflater extends
     }
 
     @Override
-    public int inflate(Pointer b, long off, int len)
+    public int inflate(long b, int len)
         throws InvalidArgumentException,
                InvalidDataException,
                OutOfMemoryError
     {
+        if (b == 0L)
+            throw new NullPointerException();
         if (len < 1)
             throw new InvalidArgumentException();
-        if (off < 0L || off + len > b.sizeof())
-            throw new IndexOutOfBoundsException();
         if (handle == 0L)
             throw new IllegalStateException();
-        return inflate3(handle, b.address() + off, len);
+        return inflate3(handle, b, len);
     }
 
 }


Reply via email to