Back to index

lightning-sunbird  0.9+nobinonly
nsMsgKeySet.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "msgCore.h"    // precompiled header...
00039 #include "prlog.h"
00040 
00041 #include "MailNewsTypes.h"
00042 #include "nsMsgKeySet.h"
00043 #include "prprf.h"
00044 #include "prmem.h"
00045 #include "nsMsgKeyArray.h"
00046 
00047 #if defined(DEBUG_seth_) || defined(DEBUG_sspitzer_)
00048 #define DEBUG_MSGKEYSET 1
00049 #endif
00050 
00051 /* A compressed encoding for sets of article.  This is usually for lines from
00052    the newsrc, which have article lists like
00053 
00054    1-29627,29635,29658,32861-32863
00055 
00056    so the data has these properties:
00057 
00058    - strictly increasing
00059    - large subsequences of monotonically increasing ranges
00060    - gaps in the set are usually small, but not always
00061    - consecutive ranges tend to be large
00062 
00063    The biggest win is to run-length encode the data, storing ranges as two
00064    numbers (start+length or start,end). We could also store each number as a
00065    delta from the previous number for further compression, but that gets kind
00066    of tricky, since there are no guarentees about the sizes of the gaps, and
00067    we'd have to store variable-length words.
00068 
00069    Current data format:
00070 
00071    DATA := SIZE [ CHUNK ]*
00072    CHUNK := [ RANGE | VALUE ]
00073    RANGE := -LENGTH START
00074    START := VALUE
00075    LENGTH := PRInt32
00076    VALUE := a literal positive integer, for now
00077    it could also be an offset from the previous value.
00078    LENGTH could also perhaps be a less-than-32-bit quantity,
00079    at least most of the time.
00080 
00081    Lengths of CHUNKs are stored negative to distinguish the beginning of
00082    a chunk from a literal: negative means two-word sequence, positive
00083    means one-word sequence.
00084 
00085    0 represents a literal 0, but should not occur, and should never occur
00086    except in the first position.
00087 
00088    A length of -1 won't occur either, except temporarily - a sequence of
00089    two elements is represented as two literals, since they take up the same
00090    space.
00091 
00092    Another optimization we make is to notice that we typically ask the
00093    question ``is N a member of the set'' for increasing values of N. So the
00094    set holds a cache of the last value asked for, and can simply resume the
00095    search from there.  */
00096 
00097 MOZ_DECL_CTOR_COUNTER(nsMsgKeySet)
00098 
00099 
00100 nsMsgKeySet::nsMsgKeySet(/* MSG_NewsHost* host*/)
00101 {
00102        MOZ_COUNT_CTOR(nsMsgKeySet);
00103        m_cached_value = -1;
00104        m_cached_value_index = 0;
00105        m_length = 0;
00106        m_data_size = 10;
00107        m_data = (PRInt32 *) PR_Malloc (sizeof (PRInt32) * m_data_size);
00108 #ifdef NEWSRC_DOES_HOST_STUFF
00109        m_host = host;
00110 #endif
00111 }
00112 
00113 
00114 nsMsgKeySet::~nsMsgKeySet()
00115 {
00116        MOZ_COUNT_DTOR(nsMsgKeySet);
00117        PR_FREEIF(m_data);
00118 }
00119 
00120 
00121 PRBool nsMsgKeySet::Grow()
00122 {
00123        PRInt32 new_size;
00124        PRInt32 *new_data;
00125        new_size = m_data_size * 2;
00126        new_data = (PRInt32 *) PR_REALLOC (m_data, sizeof (PRInt32) * new_size);
00127        if (! new_data)
00128               return PR_FALSE;
00129        m_data_size = new_size;
00130        m_data = new_data;
00131        return PR_TRUE;
00132 }
00133 
00134 
00135 nsMsgKeySet::nsMsgKeySet(const char* numbers /* , MSG_NewsHost* host */)
00136 {
00137        PRInt32 *head, *tail, *end;
00138     MOZ_COUNT_CTOR(nsMsgKeySet);
00139 
00140 #ifdef NEWSRC_DOES_HOST_STUFF
00141        m_host = host;
00142 #endif
00143        m_cached_value = -1;
00144        m_cached_value_index = 0;
00145        m_length = 0;
00146        m_data_size = 10;
00147        m_data = (PRInt32 *) PR_Malloc (sizeof (PRInt32) * m_data_size);
00148        if (!m_data) return;
00149 
00150        head = m_data;
00151        tail = head;
00152        end = head + m_data_size;
00153 
00154        if(!numbers) {
00155               return;
00156        }
00157 
00158        while (nsCRT::IsAsciiSpace (*numbers)) numbers++;
00159        while (*numbers) {
00160               PRInt32 from = 0;
00161               PRInt32 to;
00162 
00163               if (tail >= end - 4) {
00164                      /* out of room! */
00165                      PRInt32 tailo = tail - head;
00166                      if (!Grow()) {
00167                             PR_FREEIF(m_data);
00168                             return;
00169                      }
00170                      /* data may have been relocated */
00171                      head = m_data;
00172                      tail = head + tailo;
00173                      end = head + m_data_size;
00174               }
00175 
00176               while (nsCRT::IsAsciiSpace(*numbers)) numbers++;
00177               if (*numbers && !nsCRT::IsAsciiDigit(*numbers)) {
00178                      break;               /* illegal character */
00179               }
00180               while (nsCRT::IsAsciiDigit (*numbers)) {
00181                      from = (from * 10) + (*numbers++ - '0');
00182               }
00183               while (nsCRT::IsAsciiSpace (*numbers)) numbers++;
00184               if (*numbers != '-') {
00185                      to = from;
00186               } else {
00187                      to = 0;
00188                      numbers++;
00189                      while (*numbers >= '0' && *numbers <= '9')
00190                             to = (to * 10) + (*numbers++ - '0');
00191                      while (nsCRT::IsAsciiSpace (*numbers)) numbers++;
00192               }
00193 
00194               if (to < from) to = from; /* illegal */
00195 
00196               /* This is a hack - if the newsrc file specifies a range 1-x as
00197                  being read, we internally pretend that article 0 is read as well.
00198                  (But if only 2-x are read, then 0 is not read.)  This is needed
00199                  because some servers think that article 0 is an article (I think)
00200                  but some news readers (including Netscape 1.1) choke if the .newsrc
00201                  file has lines beginning with 0...   ### */
00202               if (from == 1) from = 0;
00203 
00204               if (to == from) {
00205                      /* Write it as a literal */
00206                      *tail = from;
00207                      tail++;
00208               } else /* Write it as a range. */ {
00209                      *tail = -(to - from);
00210                      tail++;
00211                      *tail = from;
00212                      tail++;
00213               }
00214 
00215               while (*numbers == ',' || nsCRT::IsAsciiSpace (*numbers)) {
00216                      numbers++;
00217               }
00218        }
00219 
00220        m_length = tail - head; /* size of data */
00221 }
00222 
00223 
00224 
00225 nsMsgKeySet*
00226 nsMsgKeySet::Create(/*MSG_NewsHost* host*/)
00227 {
00228   nsMsgKeySet* set = new nsMsgKeySet(/* host */);
00229        if (set && set->m_data == NULL) {
00230               delete set;
00231               set = NULL;
00232        }
00233        return set;
00234 }
00235 
00236 
00237 nsMsgKeySet*
00238 nsMsgKeySet::Create(const char* value /* , MSG_NewsHost* host */)
00239 {
00240 #ifdef DEBUG_MSGKEYSET
00241     printf("create from %s\n",value);
00242 #endif
00243 
00244        nsMsgKeySet* set = new nsMsgKeySet(value /* , host */);
00245        if (set && set->m_data == NULL) {
00246               delete set;
00247               set = NULL;
00248        }
00249        return set;
00250 }
00251 
00252 
00253 
00254 /* Returns the lowest non-member of the set greater than 0.
00255  */
00256 PRInt32
00257 nsMsgKeySet::FirstNonMember ()
00258 {
00259        if (m_length <= 0) {
00260               return 1;
00261        } else if(m_data[0] < 0 && m_data[1] != 1 && m_data[1] != 0) {
00262               /* first range not equal to 0 or 1, always return 1 */
00263               return 1;
00264        } else if (m_data[0] < 0) {
00265               /* it's a range */
00266               /* If there is a range [N-M] we can presume that M+1 is not in the
00267                  set. */
00268               return (m_data[1] - m_data[0] + 1);
00269        } else {
00270               /* it's a literal */
00271               if (m_data[0] == 1) {
00272                      /* handle "1,..." */
00273                      if (m_length > 1 && m_data[1] == 2) {
00274                             /* This is "1,2,M-N,..." or "1,2,M,..."  where M >= 4.  Note
00275                                that M will never be 3, because in that case we would have
00276                                started with a range: "1-3,..." */
00277                             return 3;
00278                      } else {
00279                             return 2;              /* handle "1,M-N,.." or "1,M,..."
00280                                                                         where M >= 3; */
00281                      }
00282               }
00283               else if (m_data[0] == 0) {
00284                      /* handle "0,..." */
00285                      if (m_length > 1 && m_data[1] == 1) {
00286                             /* this is 0,1, (see above) */
00287                             return 2;
00288                      }
00289                      else {
00290                             return 1;
00291                      }
00292 
00293               } else {
00294                      /* handle "M,..." where M >= 2. */
00295                      return 1;
00296               }
00297        }
00298 }
00299 
00300 
00301 nsresult 
00302 nsMsgKeySet::Output(char **outputStr)
00303 {
00304   NS_ENSURE_ARG(outputStr);
00305        PRInt32 size;
00306        PRInt32 *head;
00307        PRInt32 *tail;
00308        PRInt32 *end;
00309        PRInt32 s_size;
00310        char *s_head;
00311        char *s, *s_end;
00312        PRInt32 last_art = -1;
00313 
00314   *outputStr = nsnull;
00315 
00316        size = m_length;
00317        head = m_data;
00318        tail = head;
00319        end = head + size;
00320 
00321        s_size = (size * 12) + 10;  // dmb - try to make this allocation get used at least once.
00322   s_head = (char *) nsMemory::Alloc(s_size);
00323        if (! s_head) return NS_ERROR_OUT_OF_MEMORY;
00324 
00325        s_head[0] = '\0';                  // otherwise, s_head will contain garbage.
00326        s = s_head;
00327        s_end = s + s_size;
00328 
00329        while (tail < end) {
00330               PRInt32 from;
00331               PRInt32 to;
00332 
00333               if (s > (s_end - (12 * 2 + 10))) { /* 12 bytes for each number (enough
00334                                                                                for "2147483647" aka 2^31-1),
00335                                                                                plus 10 bytes of slop. */
00336                      PRInt32 so = s - s_head;
00337                      s_size += 200;
00338                      char* tmp = (char *) nsMemory::Alloc(s_size);
00339                      if (tmp) PL_strcpy(tmp, s_head);
00340       nsMemory::Free(s_head);
00341                      s_head = tmp;
00342                      if (!s_head) return NS_ERROR_OUT_OF_MEMORY;
00343                      s = s_head + so;
00344                      s_end = s_head + s_size;
00345               }
00346 
00347               if (*tail < 0) {
00348                      /* it's a range */
00349                      from = tail[1];
00350                      to = from + (-(tail[0]));
00351                      tail += 2;
00352               }
00353               else /* it's a literal */
00354                      {
00355                             from = *tail;
00356                             to = from;
00357                             tail++;
00358                      }
00359               if (from == 0) {
00360                      from = 1;                          /* See 'hack' comment above  ### */
00361               }
00362               if (from <= last_art) from = last_art + 1;
00363               if (from <= to) {
00364                      if (from < to) {
00365                             PR_snprintf(s, s_end - s, "%lu-%lu,", from, to);
00366                      } else {
00367                             PR_snprintf(s, s_end - s, "%lu,", from);
00368                      }
00369                      s += PL_strlen(s);
00370                      last_art = to;
00371               }
00372        }
00373        if (last_art >= 0) {
00374               s--;                                             /* Strip off the last ',' */
00375        }
00376 
00377        *s = 0;
00378 
00379        *outputStr = s_head;
00380   return NS_OK;
00381 }
00382 
00383 PRInt32 
00384 nsMsgKeySet::GetLastMember()
00385 {
00386        if (m_length > 1)
00387        {
00388               PRInt32 nextToLast = m_data[m_length - 2];
00389               if (nextToLast < 0)  // is range at end?
00390               {
00391                      PRInt32 last = m_data[m_length - 1];
00392                      return (-nextToLast + last - 1);
00393               }
00394               else   // no, so last number must be last member
00395               {
00396                      return m_data[m_length - 1];
00397               }
00398        }
00399        else if (m_length == 1)
00400               return m_data[0];    // must be only 1 read.
00401        else
00402               return 0;
00403 }
00404 
00405 void nsMsgKeySet::SetLastMember(PRInt32 newHighWaterMark)
00406 {
00407        if (newHighWaterMark < GetLastMember())
00408        {
00409               while (PR_TRUE)
00410               {
00411                      if (m_length > 1)
00412                      {
00413                             PRInt32 nextToLast = m_data[m_length - 2];
00414                             PRInt32 curHighWater;
00415                             if (nextToLast < 0)  // is range at end?
00416                             {
00417                                    PRInt32 rangeStart = m_data[m_length - 1];
00418                                    PRInt32 rangeLength = -nextToLast;
00419                                    curHighWater = (rangeLength + rangeStart - 1);
00420                                    if (curHighWater > newHighWaterMark)
00421                                    {
00422                                           if (rangeStart > newHighWaterMark) 
00423                                           {
00424                                                  m_length -= 2;       // throw away whole range
00425                                           }
00426                                           else if (rangeStart == newHighWaterMark)
00427                                           {
00428                                                  // turn range into single element.
00429                                                  m_data[m_length - 2] = newHighWaterMark;
00430                                                  m_length--;
00431                                                  break;
00432                                           }
00433                                           else   // just shorten range
00434                                           {
00435                                                  m_data[m_length - 2] = -(newHighWaterMark - rangeStart);
00436                                                  break;
00437                                           }
00438                                    }
00439                                    else {
00440                                           // prevent the infinite loop
00441                                           // see bug #13062
00442                                           break;
00443                                    }      
00444                             }
00445                             else if (m_data[m_length - 1] > newHighWaterMark)       // no, so last number must be last member
00446                             {
00447                                    m_length--;
00448                             }
00449                             else
00450                                    break;
00451                      }
00452                      else 
00453                             break;
00454               }
00455               // well, the whole range is probably invalid, because the server probably re-ordered ids, 
00456               // but what can you do?
00457 #ifdef NEWSRC_DOES_HOST_STUFF
00458               if (m_host) 
00459                      m_host->MarkDirty();
00460 #endif
00461        }
00462 }
00463 
00464 PRInt32 
00465 nsMsgKeySet::GetFirstMember()
00466 {
00467        if (m_length > 1)
00468        {
00469               PRInt32 first = m_data[0];
00470               if (first < 0)       // is range at start?
00471               {
00472                      PRInt32 second = m_data[1];
00473                      return (second);
00474               }
00475               else   // no, so first number must be first member
00476               {
00477                      return m_data[0];
00478               }
00479        }
00480        else if (m_length == 1)
00481               return m_data[0];    // must be only 1 read.
00482        else
00483               return 0;
00484 }
00485 
00486 /* Re-compresses a `nsMsgKeySet' object.
00487 
00488    The assumption is made that the `nsMsgKeySet' is syntactically correct
00489    (all ranges have a length of at least 1, and all values are non-
00490    decreasing) but will optimize the compression, for example, merging
00491    consecutive literals or ranges into one range.
00492 
00493    Returns PR_TRUE if successful, PR_FALSE if there wasn't enough memory to
00494    allocate scratch space.
00495 
00496    #### This should be changed to modify the buffer in place.
00497 
00498    Also note that we never call Optimize() unless we actually changed
00499    something, so it's a great place to tell the MSG_NewsHost* that something
00500    changed.
00501    */
00502 PRBool
00503 nsMsgKeySet::Optimize()
00504 {
00505        PRInt32 input_size;
00506        PRInt32 output_size;
00507        PRInt32 *input_tail;
00508        PRInt32 *output_data;
00509        PRInt32 *output_tail;
00510        PRInt32 *input_end;
00511        PRInt32 *output_end;
00512 
00513        input_size = m_length;
00514        output_size = input_size + 1;
00515        input_tail = m_data;
00516        output_data = (PRInt32 *) PR_Malloc (sizeof (PRInt32) * output_size);
00517        if (!output_data) return PR_FALSE;
00518 
00519        output_tail = output_data;
00520        input_end = input_tail + input_size;
00521        output_end = output_data + output_size;
00522 
00523        /* We're going to modify the set, so invalidate the cache. */
00524        m_cached_value = -1;
00525 
00526        while (input_tail < input_end) {
00527               PRInt32 from, to;
00528               PRBool range_p = (*input_tail < 0);
00529 
00530               if (range_p) {
00531                      /* it's a range */
00532                      from = input_tail[1];
00533                      to = from + (-(input_tail[0]));
00534 
00535                      /* Copy it over */
00536                      *output_tail++ = *input_tail++;
00537                      *output_tail++ = *input_tail++;                         
00538               } else {
00539                      /* it's a literal */
00540                      from = *input_tail;
00541                      to = from;
00542 
00543                      /* Copy it over */
00544                      *output_tail++ = *input_tail++;
00545               }
00546               NS_ASSERTION(output_tail < output_end, "invalid end of output string");
00547               if (output_tail >= output_end) {
00548                      PR_Free(output_data);
00549                      return PR_FALSE;
00550               }
00551 
00552               /* As long as this chunk is followed by consecutive chunks,
00553                  keep extending it. */
00554               while (input_tail < input_end &&
00555                         ((*input_tail > 0 && /* literal... */
00556                              *input_tail == to + 1) || /* ...and consecutive, or */
00557                             (*input_tail <= 0 && /* range... */
00558                              input_tail[1] == to + 1)) /* ...and consecutive. */
00559                         ) {
00560                      if (! range_p) {
00561                             /* convert the literal to a range. */
00562                             output_tail++;
00563                             output_tail [-2] = 0;
00564                             output_tail [-1] = from;
00565                             range_p = PR_TRUE;
00566                      }
00567 
00568                      if (*input_tail > 0) { /* literal */
00569                             output_tail[-2]--; /* increase length by 1 */
00570                             to++;
00571                             input_tail++;
00572                      } else {
00573                             PRInt32 L2 = (- *input_tail) + 1;
00574                             output_tail[-2] -= L2; /* increase length by N */
00575                             to += L2;
00576                             input_tail += 2;
00577                      }
00578               }
00579        }
00580 
00581        PR_Free (m_data);
00582        m_data = output_data;
00583        m_data_size = output_size;
00584        m_length = output_tail - output_data;
00585 
00586        /* One last pass to turn [N - N+1] into [N, N+1]. */
00587        output_tail = output_data;
00588        output_end = output_tail + m_length;
00589        while (output_tail < output_end) {
00590               if (*output_tail < 0) {
00591                      /* it's a range */
00592                      if (output_tail[0] == -1) {
00593                             output_tail[0] = output_tail[1];
00594                             output_tail[1]++;
00595                      }
00596                      output_tail += 2;
00597               } else {
00598                      /* it's a literal */
00599                      output_tail++;
00600               }
00601        }
00602 
00603 #ifdef NEWSRC_DOES_HOST_STUFF
00604        if (m_host) m_host->MarkDirty();
00605 #endif
00606        return PR_TRUE;
00607 }
00608 
00609 
00610 
00611 PRBool
00612 nsMsgKeySet::IsMember(PRInt32 number)
00613 {
00614        PRBool value = PR_FALSE;
00615        PRInt32 size;
00616        PRInt32 *head;
00617        PRInt32 *tail;
00618        PRInt32 *end;
00619 
00620        size = m_length;
00621        head = m_data;
00622        tail = head;
00623        end = head + size;
00624 
00625        /* If there is a value cached, and that value is smaller than the
00626           value we're looking for, skip forward that far. */
00627        if (m_cached_value > 0 &&
00628               m_cached_value < number) {
00629               tail += m_cached_value_index;
00630        }
00631 
00632        while (tail < end) {
00633               if (*tail < 0) {
00634                      /* it's a range */
00635                      PRInt32 from = tail[1];
00636                      PRInt32 to = from + (-(tail[0]));
00637                      if (from > number) {
00638                             /* This range begins after the number - we've passed it. */
00639                             value = PR_FALSE;
00640                             goto DONE;
00641                      } else if (to >= number) {
00642                             /* In range. */
00643                             value = PR_TRUE;
00644                             goto DONE;
00645                      } else {
00646                             tail += 2;
00647                      }
00648               }
00649               else {
00650                      /* it's a literal */
00651                      if (*tail == number) {
00652                             /* bang */
00653                             value = PR_TRUE;
00654                             goto DONE;
00655                      } else if (*tail > number) {
00656                             /* This literal is after the number - we've passed it. */
00657                             value = PR_FALSE;
00658                             goto DONE;
00659                      } else {
00660                             tail++;
00661                      }
00662               }
00663        }
00664 
00665 DONE:
00666        /* Store the position of this chunk for next time. */
00667        m_cached_value = number;
00668        m_cached_value_index = tail - head;
00669 
00670        return value;
00671 }
00672 
00673 
00674 int
00675 nsMsgKeySet::Add(PRInt32 number)
00676 {
00677        PRInt32 size;
00678        PRInt32 *head;
00679        PRInt32 *tail;
00680        PRInt32 *end;
00681 
00682 #ifdef DEBUG_MSGKEYSET
00683     printf("add %d\n",number);
00684 #endif
00685     
00686        size = m_length;
00687        head = m_data;
00688        tail = head;
00689        end = head + size;
00690 
00691        NS_ASSERTION (number >= 0, "can't have negative items");
00692        if (number < 0)
00693               return 0;
00694 
00695        /* We're going to modify the set, so invalidate the cache. */
00696        m_cached_value = -1;
00697 
00698        while (tail < end) {
00699               if (*tail < 0) {
00700                      /* it's a range */
00701                      PRInt32 from = tail[1];
00702                      PRInt32 to = from + (-(tail[0]));
00703 
00704                      if (from <= number && to >= number) {
00705                             /* This number is already present - we don't need to do
00706                                anything. */
00707                             return 0;
00708                      }
00709 
00710                      if (to > number) {
00711                             /* We have found the point before which the new number
00712                                should be inserted. */
00713                             break;
00714                      }
00715 
00716                      tail += 2;
00717               } else {
00718                      /* it's a literal */
00719                      if (*tail == number) {
00720                             /* This number is already present - we don't need to do
00721                                anything. */
00722                             return 0;
00723                      }
00724 
00725                      if (*tail > number) {
00726                             /* We have found the point before which the new number
00727                                should be inserted. */
00728                             break;
00729                      }
00730 
00731                      tail++;
00732               }
00733        }
00734 
00735        /* At this point, `tail' points to a position in the set which represents
00736           a value greater than `new'; or it is at `end'. In the interest of
00737           avoiding massive duplication of code, simply insert a literal here and
00738           then run the optimizer.
00739           */
00740        int mid = (tail - head); 
00741 
00742        if (m_data_size <= m_length + 1) {
00743               int endo = end - head;
00744               if (!Grow()) {
00745                      return NS_ERROR_OUT_OF_MEMORY;
00746               }
00747               head = m_data;
00748               end = head + endo;
00749        }
00750 
00751        if (tail == end) {
00752               /* at the end */
00753               /* Add a literal to the end. */
00754               m_data[m_length++] = number;
00755        } else {
00756               /* need to insert (or edit) in the middle */
00757               PRInt32 i;
00758               for (i = size; i > mid; i--) {
00759                      m_data[i] = m_data[i-1];
00760               }
00761               m_data[i] = number;
00762               m_length++;
00763        }
00764 
00765        Optimize();
00766        return 1;
00767 }
00768 
00769 
00770 
00771 int
00772 nsMsgKeySet::Remove(PRInt32 number)
00773 {
00774        PRInt32 size;
00775        PRInt32 *head;
00776        PRInt32 *tail;
00777        PRInt32 *end;
00778 #ifdef DEBUG_MSGKEYSET
00779     printf("remove %d\n",number);
00780 #endif
00781 
00782        size = m_length;
00783        head = m_data;
00784        tail = head;
00785        end = head + size;
00786 
00787        // **** I am not sure this is a right thing to comment the following
00788        // statements out. The reason for this is due to the implementation of
00789        // offline save draft and template. We use faked UIDs (negative ids) for
00790        // offline draft and template in order to distinguish them from real
00791        // UID. David I need your help here. **** jt
00792 
00793        // PR_ASSERT(number >= 0);
00794        // if (number < 0) {
00795        //     return -1;
00797 
00798        /* We're going to modify the set, so invalidate the cache. */
00799        m_cached_value = -1;
00800 
00801        while (tail < end) {
00802               PRInt32 mid = (tail - m_data);
00803 
00804               if (*tail < 0) {
00805                      /* it's a range */
00806                      PRInt32 from = tail[1];
00807                      PRInt32 to = from + (-(tail[0]));
00808 
00809                      if (number < from || number > to) {
00810                             /* Not this range */
00811                             tail += 2;
00812                             continue;
00813                      }
00814 
00815                      if (to == from + 1) {
00816                             /* If this is a range [N - N+1] and we are removing M
00817                                (which must be either N or N+1) replace it with a
00818                                literal. This reduces the length by 1. */
00819                             m_data[mid] = (number == from ? to : from);
00820                             while (++mid < m_length) {
00821                                    m_data[mid] = m_data[mid+1];
00822                             }
00823                             m_length--;
00824                             Optimize();
00825                             return 1;
00826                      } else if (to == from + 2) {
00827                             /* If this is a range [N - N+2] and we are removing M,
00828                                replace it with the literals L,M (that is, either
00829                                (N, N+1), (N, N+2), or (N+1, N+2). The overall
00830                                length remains the same. */
00831                             m_data[mid] = from;
00832                             m_data[mid+1] = to;
00833                             if (from == number) {
00834                                    m_data[mid] = from+1;
00835                             } else if (to == number) {
00836                                    m_data[mid+1] = to-1;
00837                             }
00838                             Optimize();
00839                             return 1;
00840                      } else if (from == number) {
00841                             /* This number is at the beginning of a long range (meaning a
00842                                range which will still be long enough to remain a range.)
00843                                Increase start and reduce length of the range. */
00844                             m_data[mid]++;
00845                             m_data[mid+1]++;
00846                             Optimize();
00847                             return 1;
00848                      } else if (to == number) {
00849                             /* This number is at the end of a long range (meaning a range
00850                                which will still be long enough to remain a range.)
00851                                Just decrease the length of the range. */
00852                             m_data[mid]++;
00853                             Optimize();
00854                             return 1;
00855                      } else {
00856                             /* The number being deleted is in the middle of a range which
00857                                must be split. This increases overall length by 2.
00858                                */
00859                             PRInt32 i;
00860                             int endo = end - head;
00861                             if (m_data_size - m_length <= 2) {
00862                                    if (!Grow()) return NS_ERROR_OUT_OF_MEMORY;
00863                             }
00864                             head = m_data;
00865                             end = head + endo;
00866 
00867                             for (i = m_length + 2; i > mid + 2; i--) {
00868                                    m_data[i] = m_data[i-2];
00869                             }
00870 
00871                             m_data[mid] = (- (number - from - 1));
00872                             m_data[mid+1] = from;
00873                             m_data[mid+2] = (- (to - number - 1));
00874                             m_data[mid+3] = number + 1;
00875                             m_length += 2;
00876 
00877                             /* Oops, if we've ended up with a range with a 0 length,
00878                                which is illegal, convert it to a literal, which reduces
00879                                the overall length by 1. */
00880                             if (m_data[mid] == 0) {
00881                                    /* first range */
00882                                    m_data[mid] = m_data[mid+1];
00883                                    for (i = mid + 1; i < m_length; i++) {
00884                                           m_data[i] = m_data[i+1];
00885                                    }
00886                                    m_length--;
00887                             }
00888                             if (m_data[mid+2] == 0) {
00889                                    /* second range */
00890                                    m_data[mid+2] = m_data[mid+3];
00891                                    for (i = mid + 3; i < m_length; i++) {
00892                                           m_data[i] = m_data[i+1];
00893                                    }
00894                                    m_length--;
00895                             }
00896                             Optimize();
00897                             return 1;
00898                      }
00899               } else {
00900                      /* it's a literal */
00901                      if (*tail != number) {
00902                             /* Not this literal */
00903                             tail++;
00904                             continue;
00905                      }
00906 
00907                      /* Excise this literal. */
00908                      m_length--;
00909                      while (mid < m_length) {
00910                             m_data[mid] = m_data[mid+1];
00911                             mid++;
00912                      }
00913                      Optimize();
00914                      return 1;
00915               }
00916        }
00917 
00918        /* It wasn't here at all. */
00919        return 0;
00920 }
00921 
00922 
00923 static PRInt32*
00924 msg_emit_range(PRInt32* tmp, PRInt32 a, PRInt32 b)
00925 {
00926        if (a == b) {
00927               *tmp++ = a;
00928        } else {
00929               NS_ASSERTION(a < b && a >= 0, "range is out of order");
00930               *tmp++ = -(b - a);
00931               *tmp++ = a;
00932        }
00933        return tmp;
00934 }
00935 
00936 
00937 int
00938 nsMsgKeySet::AddRange(PRInt32 start, PRInt32 end)
00939 {
00940        PRInt32 tmplength;
00941        PRInt32* tmp;
00942        PRInt32* in;
00943        PRInt32* out;
00944        PRInt32* tail;
00945        PRInt32 a;
00946        PRInt32 b;
00947        PRBool didit = PR_FALSE;
00948 
00949        /* We're going to modify the set, so invalidate the cache. */
00950        m_cached_value = -1;
00951 
00952        NS_ASSERTION(start <= end, "invalid range");
00953        if (start > end) return -1;
00954 
00955        if (start == end) {
00956               return Add(start);
00957        }
00958 
00959        tmplength = m_length + 2;
00960        tmp = (PRInt32*) PR_Malloc(sizeof(PRInt32) * tmplength);
00961 
00962        if (!tmp) return NS_ERROR_OUT_OF_MEMORY;
00963 
00964        in = m_data;
00965        out = tmp;
00966        tail = in + m_length;
00967 
00968 #define EMIT(x, y) out = msg_emit_range(out, x, y)
00969 
00970        while (in < tail) {
00971               // Set [a,b] to be this range.
00972               if (*in < 0) {
00973                      b = - *in++;
00974                      a = *in++;
00975                      b += a;
00976               } else {
00977                      a = b = *in++;
00978               }
00979 
00980               if (a <= start && b >= end) {
00981                      // We already have the entire range marked.
00982                      PR_Free(tmp);
00983                      return 0;
00984               }
00985               if (start > b + 1) {
00986                      // No overlap yet.
00987                      EMIT(a, b);
00988               } else if (end < a - 1) {
00989                      // No overlap, and we passed it.
00990                      EMIT(start, end);
00991                      EMIT(a, b);
00992                      didit = PR_TRUE;
00993                      break;
00994               } else {
00995                      // The ranges overlap.  Suck this range into our new range, and
00996                      // keep looking for other ranges that might overlap.
00997                      start = start < a ? start : a;
00998                      end = end > b ? end : b;
00999               }
01000        }
01001        if (!didit) EMIT(start, end);
01002        while (in < tail) {
01003               *out++ = *in++;
01004        }
01005 
01006 #undef EMIT
01007 
01008        PR_Free(m_data);
01009        m_data = tmp;
01010        m_length = out - tmp;
01011        m_data_size = tmplength;
01012 #ifdef NEWSRC_DOES_HOST_STUFF
01013        if (m_host) m_host->MarkDirty();
01014 #endif
01015        return 1;
01016 }
01017 
01018 PRInt32
01019 nsMsgKeySet::CountMissingInRange(PRInt32 range_start, PRInt32 range_end)
01020 {
01021        PRInt32 count;
01022        PRInt32 *head;
01023        PRInt32 *tail;
01024        PRInt32 *end;
01025 
01026        NS_ASSERTION (range_start >= 0 && range_end >= 0 && range_end >= range_start, "invalid range");
01027        if (range_start < 0 || range_end < 0 || range_end < range_start) return -1;
01028 
01029        head = m_data;
01030        tail = head;
01031        end = head + m_length;
01032 
01033        count = range_end - range_start + 1;
01034 
01035        while (tail < end) {
01036               if (*tail < 0) {
01037                      /* it's a range */
01038                      PRInt32 from = tail[1];
01039                      PRInt32 to = from + (-(tail[0]));
01040                      if (from < range_start) from = range_start;
01041                      if (to > range_end) to = range_end;
01042 
01043                      if (to >= from)
01044                             count -= (to - from + 1);
01045 
01046                      tail += 2;
01047               } else {
01048                      /* it's a literal */
01049                      if (*tail >= range_start && *tail <= range_end) count--;
01050                      tail++;
01051               }
01052               NS_ASSERTION (count >= 0, "invalid count");
01053        }
01054        return count;
01055 }
01056 
01057 
01058 int 
01059 nsMsgKeySet::FirstMissingRange(PRInt32 min, PRInt32 max,
01060                                                           PRInt32* first, PRInt32* last)
01061 {
01062        PRInt32 size;
01063        PRInt32 *head;
01064        PRInt32 *tail;
01065        PRInt32 *end;
01066        PRInt32 from = 0;
01067        PRInt32 to = 0;
01068        PRInt32 a;
01069        PRInt32 b;
01070 
01071        NS_ASSERTION(first && last, "invalid parameter");
01072        if (!first || !last) return -1;
01073 
01074        *first = *last = 0;
01075 
01076        NS_ASSERTION(min <= max && min > 0, "invalid min or max param");
01077        if (min > max || min <= 0) return -1;
01078 
01079        size = m_length;
01080        head = m_data;
01081        tail = head;
01082        end = head + size;
01083 
01084        while (tail < end) {
01085               a = to + 1;
01086               if (*tail < 0) {                   /* We got a range. */
01087                      from = tail[1];
01088                      to = from + (-(tail[0]));
01089                      tail += 2;
01090               } else {
01091                      from = to = tail[0];
01092                      tail++;
01093               }
01094               b = from - 1;
01095               /* At this point, [a,b] is the range of unread articles just before
01096                  the current range of read articles [from,to].  See if this range
01097                  intersects the [min,max] range we were given. */
01098               if (a > max) return 0;      /* It's hopeless; there are none. */
01099               if (a <= b && b >= min) {
01100                      /* Ah-hah!  We found an intersection. */
01101                      *first = a > min ? a : min;
01102                      *last = b < max ? b : max;
01103                      return 0;
01104               }
01105        } 
01106        /* We found no holes in the newsrc that overlaps the range, nor did we hit
01107           something read beyond the end of the range.  So, the great infinite
01108           range of unread articles at the end of any newsrc line intersects the
01109           range we want, and we just need to return that. */
01110        a = to + 1;
01111        *first = a > min ? a : min;
01112        *last = max;
01113        return 0;
01114 }
01115 
01116 // I'm guessing we didn't include this because we didn't think we're going
01117 // to need it. I'm not so sure. I'm putting it in for now.
01118 int 
01119 nsMsgKeySet::LastMissingRange(PRInt32 min, PRInt32 max,
01120                                                           PRInt32* first, PRInt32* last)
01121 {
01122   PRInt32 size;
01123   PRInt32 *head;
01124   PRInt32 *tail;
01125   PRInt32 *end;
01126   PRInt32 from = 0;
01127   PRInt32 to = 0;
01128   PRInt32 a;
01129   PRInt32 b;
01130 
01131   NS_ASSERTION(first && last, "invalid null param");
01132   if (!first || !last) return -1;
01133 
01134   *first = *last = 0;
01135 
01136 
01137   NS_ASSERTION(min <= max && min > 0, "invalid min or max");
01138   if (min > max || min <= 0) return -1;
01139 
01140   size = m_length;
01141   head = m_data;
01142   tail = head;
01143   end = head + size;
01144 
01145   while (tail < end) {
01146        a = to + 1;
01147        if (*tail < 0) {                   /* We got a range. */
01148          from = tail[1];
01149          to = from + (-(tail[0]));
01150          tail += 2;
01151        } else {
01152          from = to = tail[0];
01153          tail++;
01154        }
01155        b = from - 1;
01156        /* At this point, [a,b] is the range of unread articles just before
01157           the current range of read articles [from,to].  See if this range
01158           intersects the [min,max] range we were given. */
01159        if (a > max) return 0;      /* We're done.  If we found something, it's already
01160                                                     sitting in [*first,*last]. */
01161        if (a <= b && b >= min) {
01162          /* Ah-hah!  We found an intersection. */
01163          *first = a > min ? a : min;
01164          *last = b < max ? b : max;
01165          /* Continue on, looking for a later range. */
01166        }
01167   }
01168   if (to < max) {
01169        /* The great infinite range of unread articles at the end of any newsrc
01170           line intersects the range we want, and we just need to return that. */
01171        a = to + 1;
01172        *first = a > min ? a : min;
01173        *last = max;
01174   }
01175   return 0;
01176 }
01177 
01184 nsresult 
01185 nsMsgKeySet::ToMsgKeyArray(nsMsgKeyArray **aArray)
01186 {
01187     PRInt32 size;
01188     PRInt32 *head;
01189     PRInt32 *tail;
01190     PRInt32 *end;
01191     PRInt32 last_art = -1;
01192 
01193     nsMsgKeyArray *array;
01194     NS_NEWXPCOM(array, nsMsgKeyArray);
01195     if (!array) 
01196         return NS_ERROR_OUT_OF_MEMORY;
01197 
01198     size = m_length;
01199     head = m_data;
01200     tail = head;
01201     end = head + size;
01202 
01203     while (tail < end) {
01204         PRInt32 from;
01205         PRInt32 to;
01206 
01207         if (*tail < 0) {
01208             /* it's a range */
01209             from = tail[1];
01210             to = from + (-(tail[0]));
01211             tail += 2;
01212         }
01213         else /* it's a literal */
01214             {
01215                 from = *tail;
01216                 to = from;
01217                 tail++;
01218             }
01219         if (from == 0) {
01220             from = 1;               /* See 'hack' comment above  ### */
01221         }
01222         if (from <= last_art) from = last_art + 1;
01223         if (from <= to) {
01224             if (from < to) {
01225                 for (PRInt32 i = from; i <= to ; ++i ) {
01226                     array->Add(i);
01227                 }
01228             } else {
01229                 array->Add(from);
01230             }
01231             last_art = to;
01232         }
01233     }
01234 
01235     *aArray = array;
01236     return NS_OK;
01237 }
01238 
01239 
01240 #ifdef DEBUG /* A lot of test cases for the above */
01241 
01242 #define countof(x) (sizeof(x) / sizeof(*(x)))
01243 
01244 void
01245 nsMsgKeySet::test_decoder (const char *string)
01246 {
01247   nsMsgKeySet set(string /* , NULL */);
01248   char* tmp;
01249   set.Output(&tmp);
01250   printf ("\t\"%s\"\t--> \"%s\"\n", string, tmp);
01251   nsMemory::Free(tmp);
01252 }
01253 
01254 
01255 #define START(STRING) \
01256   string = STRING;     \
01257   if (!(set = nsMsgKeySet::Create(string))) abort ()
01258 
01259 #define FROB(N,PUSHP)                                                               \
01260   i = N;                                                                                   \
01261   if (!(NS_SUCCEEDED(set->Output(&s)))) abort ();                                   \
01262   printf ("%3lu: %-58s %c %3lu =\n", (unsigned long)set->m_length, s, \
01263                 (PUSHP ? '+' : '-'), (unsigned long)i);                                    \
01264   nsMemory::Free(s);                                                                       \
01265   if (PUSHP                                                                                \
01266          ? set->Add(i) < 0                                                          \
01267          : set->Remove(i) < 0)                                                      \
01268        abort ();                                                                           \
01269   if (!(NS_SUCCEEDED(set->Output(&s)))) abort ();                                   \
01270   printf ("%3lu: %-58s optimized =\n", (unsigned long)set->m_length, s);     \
01271   nsMemory::Free(s);                                                                       \
01272 
01273 #define END()                                              \
01274   if (!(NS_SUCCEEDED(set->Output(&s)))) abort ();                                   \
01275   printf ("%3lu: %s\n\n", (unsigned long)set->m_length, s); \
01276   nsMemory::Free(s);                                                                       \
01277   delete set;                                              \
01278 
01279 
01280 
01281 void
01282 nsMsgKeySet::test_adder (void)
01283 {
01284   char *string;
01285   nsMsgKeySet *set;
01286   char *s;
01287   PRInt32 i;
01288 
01289   START("0-70,72-99,105,107,110-111,117-200");
01290 
01291   FROB(205, PR_TRUE);
01292   FROB(206, PR_TRUE);
01293   FROB(207, PR_TRUE);
01294   FROB(208, PR_TRUE);
01295   FROB(208, PR_TRUE);
01296   FROB(109, PR_TRUE);
01297   FROB(72, PR_TRUE);
01298 
01299   FROB(205, PR_FALSE);
01300   FROB(206, PR_FALSE);
01301   FROB(207, PR_FALSE);
01302   FROB(208, PR_FALSE);
01303   FROB(208, PR_FALSE);
01304   FROB(109, PR_FALSE);
01305   FROB(72, PR_FALSE);
01306 
01307   FROB(72, PR_TRUE);
01308   FROB(109, PR_TRUE);
01309   FROB(208, PR_TRUE);
01310   FROB(208, PR_TRUE);
01311   FROB(207, PR_TRUE);
01312   FROB(206, PR_TRUE);
01313   FROB(205, PR_TRUE);
01314 
01315   FROB(205, PR_FALSE);
01316   FROB(206, PR_FALSE);
01317   FROB(207, PR_FALSE);
01318   FROB(208, PR_FALSE);
01319   FROB(208, PR_FALSE);
01320   FROB(109, PR_FALSE);
01321   FROB(72, PR_FALSE);
01322 
01323   FROB(100, PR_TRUE);
01324   FROB(101, PR_TRUE);
01325   FROB(102, PR_TRUE);
01326   FROB(103, PR_TRUE);
01327   FROB(106, PR_TRUE);
01328   FROB(104, PR_TRUE);
01329   FROB(109, PR_TRUE);
01330   FROB(108, PR_TRUE);
01331   END();
01332 
01333   START("1-6"); FROB(7, PR_FALSE); END();
01334   START("1-6"); FROB(6, PR_FALSE); END();
01335   START("1-6"); FROB(5, PR_FALSE); END();
01336   START("1-6"); FROB(4, PR_FALSE); END();
01337   START("1-6"); FROB(3, PR_FALSE); END();
01338   START("1-6"); FROB(2, PR_FALSE); END();
01339   START("1-6"); FROB(1, PR_FALSE); END();
01340   START("1-6"); FROB(0, PR_FALSE); END();
01341 
01342   START("1-3"); FROB(1, PR_FALSE); END();
01343   START("1-3"); FROB(2, PR_FALSE); END();
01344   START("1-3"); FROB(3, PR_FALSE); END();
01345 
01346   START("1,3,5-7,9,10"); FROB(5, PR_FALSE); END();
01347   START("1,3,5-7,9,10"); FROB(6, PR_FALSE); END();
01348   START("1,3,5-7,9,10"); FROB(7, PR_FALSE); FROB(7, PR_TRUE); FROB(8, PR_TRUE);
01349   FROB (4, PR_TRUE); FROB (2, PR_FALSE); FROB (2, PR_TRUE);
01350 
01351   FROB (4, PR_FALSE); FROB (5, PR_FALSE); FROB (6, PR_FALSE); FROB (7, PR_FALSE);
01352   FROB (8, PR_FALSE); FROB (9, PR_FALSE); FROB (10, PR_FALSE); FROB (3, PR_FALSE);
01353   FROB (2, PR_FALSE); FROB (1, PR_FALSE); FROB (1, PR_FALSE); FROB (0, PR_FALSE);
01354   END();
01355 }
01356 
01357 #undef START
01358 #undef FROB
01359 #undef END
01360 
01361 
01362 
01363 #define START(STRING) \
01364   string = STRING;     \
01365   if (!(set = nsMsgKeySet::Create(string))) abort ()
01366 
01367 #define FROB(N,M)                                                                                 \
01368   i = N;                                                                                                 \
01369   j = M;                                                                                                 \
01370   if (!(NS_SUCCEEDED(set->Output(&s)))) abort ();                                   \
01371   printf ("%3lu: %-58s + %3lu-%3lu =\n", (unsigned long)set->m_length, s, (unsigned long)i, (unsigned long)j);  \
01372   nsMemory::Free(s);                                                                       \
01373   switch (set->AddRange(i, j)) {                                                    \
01374   case 0:                                                                                                \
01375        printf("(no-op)\n");                                                                \
01376        break;                                                                                            \
01377   case 1:                                                                                                \
01378        break;                                                                                            \
01379   default:                                                                                               \
01380        abort();                                                                                          \
01381   }                                                                                                             \
01382   if (!(NS_SUCCEEDED(set->Output(&s)))) abort ();                                   \
01383   printf ("%3lu: %-58s\n", (unsigned long)set->m_length, s);                                      \
01384   nsMemory::Free(s);                                                                       \
01385 
01386 
01387 #define END()                                              \
01388   if (!(NS_SUCCEEDED(set->Output(&s)))) abort ();                                   \
01389   printf ("%3lu: %s\n\n", (unsigned long)set->m_length, s); \
01390   nsMemory::Free(s);                                                                       \
01391   delete set;
01392 
01393 
01394 void
01395 nsMsgKeySet::test_ranges(void)
01396 {
01397   char *string;
01398   nsMsgKeySet *set;
01399   char *s;
01400   PRInt32 i;
01401   PRInt32 j;
01402 
01403   START("20-40,72-99,105,107,110-111,117-200");
01404 
01405   FROB(205, 208);
01406   FROB(50, 70);
01407   FROB(0, 10);
01408   FROB(112, 113);
01409   FROB(101, 101);
01410   FROB(5, 75);
01411   FROB(103, 109);
01412   FROB(2, 20);
01413   FROB(1, 9999);
01414 
01415   END();
01416 
01417 
01418 #undef START
01419 #undef FROB
01420 #undef END
01421 }
01422 
01423 
01424 
01425 
01426 #define TEST(N)                                                                \
01427   if (! with_cache) set->m_cached_value = -1;      \
01428   if (!(NS_SUCCEEDED(set->Output(&s)))) abort ();                                   \
01429   printf (" %3d = %s\n", N,                                      \
01430                 (set->IsMember(N) ? "true" : "false")); \
01431   nsMemory::Free(s);
01432 
01433 void
01434 nsMsgKeySet::test_member(PRBool with_cache)
01435 {
01436   nsMsgKeySet *set;
01437   char *s;
01438 
01439   s = "1-70,72-99,105,107,110-111,117-200";
01440   printf ("\n\nTesting %s (with%s cache)\n", s, with_cache ? "" : "out");
01441   if (!(set = Create(s))) {
01442        abort ();
01443   }
01444 
01445   TEST(-1);
01446   TEST(0);
01447   TEST(1);
01448   TEST(20);
01449   
01450   delete set;
01451   s = "0-70,72-99,105,107,110-111,117-200";
01452   printf ("\n\nTesting %s (with%s cache)\n", s, with_cache ? "" : "out");
01453   if (!(set = Create(s))) {
01454        abort ();
01455   }
01456   
01457   TEST(-1);
01458   TEST(0);
01459   TEST(1);
01460   TEST(20);
01461   TEST(69);
01462   TEST(70);
01463   TEST(71);
01464   TEST(72);
01465   TEST(73);
01466   TEST(74);
01467   TEST(104);
01468   TEST(105);
01469   TEST(106);
01470   TEST(107);
01471   TEST(108);
01472   TEST(109);
01473   TEST(110);
01474   TEST(111);
01475   TEST(112);
01476   TEST(116);
01477   TEST(117);
01478   TEST(118);
01479   TEST(119);
01480   TEST(200);
01481   TEST(201);
01482   TEST(65535);
01483 
01484   delete set;
01485 }
01486 
01487 #undef TEST
01488 
01489 
01490 // static void
01491 // test_newsrc (char *file)
01492 // {
01493 //   FILE *fp = fopen (file, "r");
01494 //   char buf [1024];
01495 //   if (! fp) abort ();
01496 //   while (fgets (buf, sizeof (buf), fp))
01497 //     {
01498 //       if (!strncmp (buf, "options ", 8))
01499 //            fwrite (buf, 1, strlen (buf), stdout);
01500 //       else
01501 //            {
01502 //              char *sep = buf;
01503 //              while (*sep != 0 && *sep != ':' && *sep != '!')
01504 //                   sep++;
01505 //              if (*sep) sep++;
01506 //              while (nsCRT::IsAsciiSpace (*sep)) sep++;
01507 //              fwrite (buf, 1, sep - buf, stdout);
01508 //              if (*sep)
01509 //                   {
01510 //                     char *s;
01511 //                     msg_NewsRCSet *set = msg_parse_newsrc_set (sep, &allocinfo);
01512 //                     if (! set)
01513 //                          abort ();
01514 //                     if (! msg_OptimizeNewsRCSet (set))
01515 //                          abort ();
01516 //                     if (! ((s = msg_format_newsrc_set (set))))
01517 //                          abort ();
01518 //                     msg_free_newsrc_set (set, &allocinfo);
01519 //                     fwrite (s, 1, strlen (s), stdout);
01520 //                     free (s);
01521 //                     fwrite ("\n", 1, 1, stdout);
01522 //                   }
01523 //            }
01524 //     }
01525 //   fclose (fp);
01526 // }
01527 
01528 void
01529 nsMsgKeySet::RunTests ()
01530 {
01531 
01532   test_decoder ("");
01533   test_decoder (" ");
01534   test_decoder ("0");
01535   test_decoder ("1");
01536   test_decoder ("123");
01537   test_decoder (" 123 ");
01538   test_decoder (" 123 4");
01539   test_decoder (" 1,2, 3, 4");
01540   test_decoder ("0-70,72-99,100,101");
01541   test_decoder (" 0-70 , 72 - 99 ,100,101 ");
01542   test_decoder ("0 - 268435455");
01543   /* This one overflows - we can't help it.
01544         test_decoder ("0 - 4294967295"); */
01545 
01546   test_adder ();
01547 
01548   test_ranges();
01549 
01550   test_member (PR_FALSE);
01551   test_member (PR_TRUE);
01552 
01553   // test_newsrc ("/u/montulli/.newsrc");
01554   /* test_newsrc ("/u/jwz/.newsrc");*/
01555 }
01556 
01557 #endif /* DEBUG */