/// <summary>
        /// Stores data to cache.
        /// 
        /// If data does not already exist for this key on the server, or if the key is being<br/>
        /// deleted, the specified value will not be stored.<br/>
        /// The server will automatically delete the value when the expiration time has been reached.<br/>
        /// <br/>
        /// If compression is enabled, and the data is longer than the compression threshold<br/>
        /// the data will be stored in compressed form.<br/>
        /// <br/>
        /// As of the current release, all objects stored will use serialization.
        /// </summary>
        /// 
        /// <param name="cmdType">action to take (set, add, replace)</param>
        /// <param name="key">key to store cache under</param>
        /// <param name="value">object to cache</param>
        /// <param name="expiry">expiration, if any</param>
        /// <param name="sock">SockIO to use</param>
        /// 
        /// <returns><code>true</code> if successful, <code>fale</code> or exception otherwise</returns>
        private bool SetItem(SetCommandType cmdType, string key, object value, DateTime expiry, SockIOPool.SockIO sock)
        {
            // store flags
            int flags = 0;

            // byte array to hold data
            byte[] val;

            if(expiry.Equals(DateTime.MinValue))
                expiry = new DateTime(0);

            // serialize the object
            // unless client request data to be not serialized
            // in which case, we will go with string representation
            // but only for a select few classes
            if (!(value is string || value is Int32 || value is Double || value is Single || value is Int64 || value is SByte || value is Int16 || value is Char || value is StringBuilder))
            {
                // we can ONLY not serialize the above classes
                // this is generally a bad idea as we should always
                // just use serialization.
                // but, it is useful for sharing data between non-.NET
                // and also for storing ints for the increment method
                if(log.IsInfoEnabled) log.Info("storing data as a string for key: " + key + " for class: " + value.GetType().FullName);

                val = UTF8Encoding.UTF8.GetBytes(value.ToString());
            }
            else
            {
                if(log.IsInfoEnabled) log.Info("serializing for key: " + key + " for class: " + value.GetType().FullName);
                try
                {
                    MemoryStream bos = new MemoryStream();
                    new BinaryFormatter().Serialize((new BinaryWriter(bos)).BaseStream, value);

                    val = bos.ToArray();
                    flags |= F_SERIALIZED;
                }
                catch(SerializationException se)
                {
                    if(log.IsErrorEnabled) log.Error("failed to serialize obj: " + value.ToString(), se);

                    // return socket to pool and bail
                    sock.Close();

            #if EXCEPTIONS
                    throw new MemcachedException(se);
            #else
                    return false;
            #endif
                }
            }

            // now try to compress if we want to
            // and if the length is over the threshold
            if (compressEnable && val.Length > compressThreshold)
            {
                if(log.IsInfoEnabled) log.Info("trying to compress data, size prior to compression: " + val.Length);

                try
                {
                    MemoryStream bos = new MemoryStream(val.Length);
                    GZipOutputStream gos = new GZipOutputStream(bos);
                    gos.Write(val, 0, val.Length);
                    gos.Close();

                    // store it and set compression flag
                    val = bos.ToArray();
                    flags |= F_COMPRESSED;

                    if(log.IsInfoEnabled) log.Info("compression succeeded, size after: " + val.Length);
                }
                // TODO Not sure which specific exception(s) is/are thrown
                catch(Exception e)
                {
                    if(log.IsErrorEnabled) log.Error("Exception while compressing stream", e);
                }
            }

            // now write the data to the cache server
            StringBuilder cmd = new StringBuilder();
            switch(cmdType)
            {
                case SetCommandType.Add:
                    cmd.Append("add");
                    break;
                case SetCommandType.Replace:
                    cmd.Append("replace");
                    break;
                case SetCommandType.Set:
                    cmd.Append("set");
                    break;
                default:
                    // As if this can actually happen
                    throw new ArgumentException("cmdType");
            }
            cmd.Append(" ");
            cmd.Append(key);
            cmd.Append(" ");
            cmd.Append(flags.ToString());
            cmd.Append(" ");
            cmd.Append(expiry.Ticks / 1000);
            cmd.Append(" ");
            cmd.Append(val.Length.ToString());
            cmd.Append("\n");

            try
            {
                sock.Write(UTF8Encoding.UTF8.GetBytes(cmd.ToString()));
                sock.Write(val);
                sock.Write(UTF8Encoding.UTF8.GetBytes("\n"));
                sock.Flush();

                // get result code
                string line = sock.ReadLine();
                if(log.IsInfoEnabled) log.Info("memcache cmd (result code): " + cmd + " (" + line + ")");

                switch(line)
                {
                    case STORED:
                        if(log.IsInfoEnabled) log.Info("data successfully stored for key: " + key);
                        return true;
                    case NOTSTORED:
                        if(log.IsInfoEnabled) log.Info("data not stored in cache for key: " + key);
            #if EXCEPTIONS
                        throw new NotStoredException(key);
            #else
                        return false;
            #endif
                    default:
                        if(log.IsErrorEnabled) log.Error("error storing data in cache for key: " + key + " -- length: " + val.Length);
            #if EXCEPTIONS
                        throw new MemcachedException(line);
            #else
                        return false;
            #endif
                }
            }
            catch(IOException ioe)
            {
                if(log.IsErrorEnabled) log.Error("exception thrown while writing bytes to server on set", ioe);

                try
                {
                    sock.TrueClose();
                }
                catch(IOException ioee)
                {
                    if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee);
                }

                sock = null;
            #if EXCEPTIONS
                throw new SocketException(e);
            #else
                return false;
            #endif
            }
            finally
            {
                if(sock != null)
                    sock.Close();
            }
        }
        /// <summary> This method loads the data from cache into a Map.
        /// 
        /// Pass a SockIO object which is ready to receive data and a HashMap<br/>
        /// to store the results.
        /// 
        /// </summary>
        /// <param name="sock">socket waiting to pass back data
        /// </param>
        /// <param name="hm">hashmap to store data into
        /// </param>
        /// <throws>  IOException if io exception happens while reading from socket </throws>
        private void LoadItems(SockIOPool.SockIO sock, IDictionary hm)
        {
            while (true)
            {
                string line = sock.ReadLine();

                if(log.IsDebugEnabled) log.Debug("line: " + line);

                if (line.StartsWith(VALUE))
                {
                    string[] info = line.Split(' ');
                    string key = info[1];
                    int flag = int.Parse(info[2]);
                    int length = int.Parse(info[3]);

                    if(log.IsDebugEnabled)
                        log.Debug("key: " + key + "\n" +
                            "flags: " + flag + "\n" +
                            "length: " + length);

                    // read obj into buffer
                    byte[] buf = new byte[length];
                    sock.Read(buf);
                    sock.ClearEOL();

                    // ready object
                    object o;

                    // check for compression
                    if ((flag & F_COMPRESSED) != 0)
                    {
                        try
                        {
                            // read the input stream, and write to a byte array output stream since
                            // we have to read into a byte array, but we don't know how large it
                            // will need to be, and we don't want to resize it a bunch
                            GZipInputStream gzi = new GZipInputStream(new MemoryStream(buf));
                            MemoryStream bos = new MemoryStream(buf.Length);

                            int count;
                            byte[] tmp = new byte[2048];
                            while ((count = gzi.Read(tmp, 0, tmp.Length)) != - 1)
                            {
                                bos.Write(tmp, 0, count);
                            }

                            // store uncompressed back to buffer
                            buf = bos.ToArray();
                            gzi.Close();
                        }
                        catch(IOException ioe)
                        {
                            if(log.IsErrorEnabled) log.Error("IOException thrown while trying to uncompress input stream for key: " + key, ioe);

                            throw new IOException("IOException thrown while trying to uncompress input stream for key: " + key);
                        }
                    }

                    // we can only take out serialized objects
                    if ((flag & F_SERIALIZED) == 0)
                    {
                        if(log.IsInfoEnabled) log.Info("this object is not a serialized object.  Stuffing into a string.");

                        o = new string(UTF8Encoding.UTF8.GetChars(buf));
                    }
                    else
                    {
                        // deserialize if the data is serialized
                        BinaryReader ois = new BinaryReader(new MemoryStream(buf));
                        try
                        {
                            o = new BinaryFormatter().Deserialize(ois.BaseStream);

                            if(log.IsInfoEnabled) log.Info("deserializing " + o.GetType().FullName);
                        }
                        catch(Exception e)
                        {
                            if(log.IsErrorEnabled) log.Error("ClassNotFoundException thrown while trying to deserialize for key: " + key, e);

                            throw new IOException("failed while trying to deserialize for key: " + key);
                        }
                    }

                    // store the object into the cache
                    hm.Add(key, o);
                }
                else if (END.Equals(line))
                {
                    if(log.IsDebugEnabled) log.Debug("finished reading from cache server");

                    break;
                }
            }
        }
        /// <summary>
        /// Get item from cache
        /// </summary>
        /// <param name="key">Key of desired item</param>
        /// <param name="sock">Socket to use</param>
        /// <returns>Desired object</returns>
        private object GetItem(string key, SockIOPool.SockIO sock)
        {
            try
            {
                StringBuilder cmd = new StringBuilder();
                cmd.Append("get ");
                cmd.Append(key);
                cmd.Append("\n");

                if(log.IsDebugEnabled) log.Debug("memcache get command: " + cmd);

                sock.Write(UTF8Encoding.UTF8.GetBytes(cmd.ToString()));
                sock.Flush();

                // build empty map
                // and fill it from server
                IDictionary hm = new Hashtable();
                LoadItems(sock, hm);

                // debug code
                if(log.IsDebugEnabled) log.Debug("memcache: got back " + hm.Count + " results");

                // return the value for this key if we found it
                // else return null

                object result = hm[key];

                if(result == null)
            #if EXCEPTIONS
                    throw new NotFoundException(key);
            #else
                    return false;
            #endif

                return result;
            }
            catch(IOException ioe)
            {
                if(log.IsErrorEnabled) log.Error("exception thrown while trying to get object from cache for key: " + key, ioe);

                try
                {
                    sock.TrueClose();
                }
                catch(IOException ioee)
                {
                    if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee);
                }

                sock = null;

            #if EXCEPTIONS
                throw new SocketException(e);
            #else
                return false;
            #endif
            }
            finally
            {
                if(sock != null)
                    sock.Close();
            }
        }
        /// <summary>
        /// Increments/decrements the value at the specified key by inc.
        /// 
        /// From Javadocs:
        /// Note that the server uses a 32-bit unsigned integer, and checks for<br/>
        /// underflow. In the event of underflow, the result will be zero.  Because<br/>
        /// java lacks unsigned types, the value is returned as a 64-bit integer.<br/>
        /// The server will only decrement a value if it already exists;<br/>
        /// if a value is not found, -1 will be returned.
        /// 
        /// .NET supports unsigned types so refactor
        /// </summary>
        /// 
        /// <param name="cmdType">increment/decrement type</param>
        /// <param name="key">cache key</param>
        /// <param name="qty">amount to incr or decr</param>
        /// <param name="sock">SockIO to use</param>
        /// 
        /// <returns> new value or -1 if not exist</returns>
        private long IncrDecrItem(IncrDecrCommandType cmdType, string key, long qty, SockIOPool.SockIO sock)
        {
            // Assumes non-null arguments

            if(log.IsDebugEnabled) log.Debug("memcache incr/decr command: " + cmdType.ToString());

            // now write the data to the cache server
            StringBuilder cmd = new StringBuilder();
            switch(cmdType)
            {
                case IncrDecrCommandType.Increment:
                    cmd.Append("incr");
                    break;
                case IncrDecrCommandType.Decrement:
                    cmd.Append("decr");
                    break;
                default:
                    // As if this can actually happen
                    throw new ArgumentException("cmdType");
            }
            cmd.Append(" ");
            cmd.Append(key);
            cmd.Append(" ");
            cmd.Append(qty.ToString());
            cmd.Append("\n");

            try
            {
                sock.Write(UTF8Encoding.UTF8.GetBytes(cmd.ToString()));
                sock.Flush();

                // get result back
                string line = sock.ReadLine();

                if(Regex.IsMatch("[^0-9-]", line))
                {
                    return long.Parse(line);
                }
                else if(NOTFOUND.Equals(line))
                {
                    if(log.IsInfoEnabled) log.Info("key not found to incr/decr for key: " + key);
            #if EXCEPTIONS
                    throw new NotFoundException();
            #else
                    // TODO Hacky
                    return -1;
            #endif
                }

                if(log.IsErrorEnabled) log.Error("error incr/decr key: " + key);
            #if EXCEPTIONS
                throw new MemcachedException(cmd.ToString());
            #else
                // TODO Hacky
                return -1;
            #endif
            }
            catch(IOException ioe)
            {
                // exception thrown
                if(log.IsErrorEnabled) log.Error("exception thrown while trying to incr/decr", ioe);

                try
                {
                    sock.TrueClose();
                }
                catch(IOException ioee)
                {
                    if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee);
                }

                sock = null;

            #if EXCEPTIONS
                throw new SocketException(e);
            #else
                return -1;
            #endif
            }
            finally
            {
                if(sock != null)
                    sock.Close();
            }
        }
        /// <summary>
        /// Delete item
        /// </summary>
        /// <param name="key">Key to delete</param>
        /// <param name="expiry">Expiration to use</param>
        /// <param name="sock">SockIO to use</param>
        /// <returns><code>true</code> if successful, <code>fale</code> or exception otherwise</returns>
        private static bool DeleteItem(string key, DateTime expiry, SockIOPool.SockIO sock)
        {
            // Assumes non-null arguments

            // build command
            StringBuilder command = new StringBuilder();
            command.Append("delete ");
            command.Append(key);
            if(!expiry.Equals(DateTime.MinValue))
            {
                command.Append(" ");
                command.Append(expiry.Ticks / 1000);
            }
            command.Append("\n");

            try
            {
                sock.Write(UTF8Encoding.UTF8.GetBytes(command.ToString()));
                sock.Flush();

                // if we get appropriate response back, then we return true
                string line = sock.ReadLine();

                switch(line)
                {
                    case DELETED:
                        return true;
                    case NOTFOUND:
            #if EXCEPTIONS
                        throw new NotFoundException();
            #else
                        return false;
            #endif
                    default:
            #if EXCEPTIONS
                        throw new MemcachedException(line);
            #else
                        return false;
            #endif
                }
            }
            catch(IOException ioe)
            {
                if(log.IsErrorEnabled) log.Error("exception thrown while writing bytes to server on delete", ioe);

                try
                {
                    sock.TrueClose();
                }
                catch(IOException ioee)
                {
                    if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee);
                }

                sock = null;

            #if EXCEPTIONS
                throw new SocketException(e);
            #else
                return false;
            #endif
            }
            finally
            {
                if(sock != null)
                    sock.Close();
            }
        }