about summary refs log tree commit diff
path: root/locale
diff options
context:
space:
mode:
Diffstat (limited to 'locale')
-rw-r--r--locale/locarchive.h2
-rw-r--r--locale/programs/locarchive.c232
2 files changed, 196 insertions, 38 deletions
diff --git a/locale/locarchive.h b/locale/locarchive.h
index f43a6b6734..e94bc2b480 100644
--- a/locale/locarchive.h
+++ b/locale/locarchive.h
@@ -88,7 +88,7 @@ struct locarhandle
 
 
 /* In memory data for the locales with their checksums.  */
-typedef struct
+typedef struct locale_category_data
 {
   off64_t size;
   void *addr;
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
index 267d7baaa4..057fab8d2e 100644
--- a/locale/programs/locarchive.c
+++ b/locale/programs/locarchive.c
@@ -167,6 +167,46 @@ create_archive (const char *archivefname, struct locarhandle *ah)
   ah->len = total;
 }
 
+
+struct oldlocrecent
+{
+  unsigned int cnt;
+  struct locrecent *locrec;
+};
+
+static int
+oldlocrecentcmp (const void *a, const void *b)
+{
+  struct locrecent *la = ((const struct oldlocrecent *) a)->locrec;
+  struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec;
+
+  if (la->record[LC_ALL].offset < lb->record[LC_ALL].offset)
+    return -1;
+  if (la->record[LC_ALL].offset > lb->record[LC_ALL].offset)
+    return 1;
+
+  if (la->record[LC_CTYPE].offset < lb->record[LC_CTYPE].offset)
+    return -1;
+  if (la->record[LC_CTYPE].offset > lb->record[LC_CTYPE].offset)
+    return 1;
+
+  if (la->record[LC_COLLATE].offset < lb->record[LC_COLLATE].offset)
+    return -1;
+  if (la->record[LC_COLLATE].offset > lb->record[LC_COLLATE].offset)
+    return 1;
+
+  if (((const struct oldlocrecent *) a)->cnt
+      < ((const struct oldlocrecent *) b)->cnt)
+    return -1;
+
+  if (((const struct oldlocrecent *) a)->cnt
+      > ((const struct oldlocrecent *) b)->cnt)
+    return 1;
+
+  return 0;
+}
+
+
 /* forward decl for below */
 static uint32_t add_locale (struct locarhandle *ah, const char *name,
 			    locale_data_t data, bool replace);
@@ -179,13 +219,14 @@ enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
   struct locarhead newhead;
   size_t total;
   void *p;
-  unsigned int cnt;
+  unsigned int cnt, loccnt;
   struct namehashent *oldnamehashtab;
   struct locrecent *oldlocrectab;
   struct locarhandle new_ah;
   size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
   char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
   char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
+  struct oldlocrecent *oldlocrecarray;
 
   if (output_prefix)
     memcpy (archivefname, output_prefix, prefix_len);
@@ -282,34 +323,46 @@ enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
 					   + head->namehash_offset);
   oldlocrectab = (struct locrecent *) ((char *) ah->addr
 				       + head->locrectab_offset);
-  for (cnt = 0; cnt < head->namehash_size; ++cnt)
+  oldlocrecarray = (struct oldlocrecent *)
+		   alloca (head->namehash_size
+			   * sizeof (struct oldlocrecent));
+
+  for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
     if (oldnamehashtab[cnt].locrec_offset != 0)
       {
-	/* Insert this entry in the new hash table.  */
-	locale_data_t old_data;
-	unsigned int idx;
-	struct locrecent *oldlocrec;
+	oldlocrecarray[loccnt].cnt = cnt;
+	oldlocrecarray[loccnt++].locrec
+	  = (struct locrecent *) ((char *) ah->addr
+				  + oldnamehashtab[cnt].locrec_offset);
+      }
 
-	oldlocrec = (struct locrecent *) ((char *) ah->addr
-					  + oldnamehashtab[cnt].locrec_offset);
+  qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
+	 oldlocrecentcmp);
 
-	for (idx = 0; idx < __LC_LAST; ++idx)
-	  if (idx != LC_ALL)
-	    {
-	      old_data[idx].size = oldlocrec->record[idx].len;
-	      old_data[idx].addr
-		= ((char *) ah->addr + oldlocrec->record[idx].offset);
+  for (cnt = 0; cnt < loccnt; ++cnt)
+    {
+      /* Insert this entry in the new hash table.  */
+      locale_data_t old_data;
+      unsigned int idx;
+      struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
 
-	      __md5_buffer (old_data[idx].addr, old_data[idx].size,
-			    old_data[idx].sum);
-	    }
+      for (idx = 0; idx < __LC_LAST; ++idx)
+	if (idx != LC_ALL)
+	  {
+	    old_data[idx].size = oldlocrec->record[idx].len;
+	    old_data[idx].addr
+	      = ((char *) ah->addr + oldlocrec->record[idx].offset);
 
-	if (add_locale (&new_ah,
-			((char *) ah->addr + oldnamehashtab[cnt].name_offset),
-			old_data, 0) == 0)
-	  error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
-      }
+	    __md5_buffer (old_data[idx].addr, old_data[idx].size,
+			  old_data[idx].sum);
+	  }
 
+      if (add_locale (&new_ah,
+	  ((char *) ah->addr
+	   + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
+	  old_data, 0) == 0)
+	error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
+    }
 
   /* Make the file globally readable.  */
   if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
@@ -487,6 +540,13 @@ insert_name (struct locarhandle *ah,
 	  break;
 	}
 
+      if (namehashtab[idx].hashval == hval)
+	{
+	  error (0, 0, "hash collision (%u) %s, %s",
+		 hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
+	}
+
+
       /* Remember the first place we can insert the new entry.  */
       if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
 	insert_idx = idx;
@@ -560,6 +620,16 @@ add_alias (struct locarhandle *ah, const char *alias, bool replace,
   namehashent->locrec_offset = locrec_offset;
 }
 
+static int			/* qsort comparator used below */
+cmpcategorysize (const void *a, const void *b)
+{
+  if (*(const void **) a == NULL)
+    return 1;
+  if (*(const void **) b == NULL)
+    return -1;
+  return ((*(const struct locale_category_data **) a)->size
+	  - (*(const struct locale_category_data **) b)->size);
+}
 
 /* Check the content of the archive for duplicates.  Add the content
    of the files if necessary.  Returns the locrec_offset.  */
@@ -575,25 +645,70 @@ add_locale (struct locarhandle *ah,
   unsigned int num_new_offsets = 0;
   struct sumhashent *sumhashtab;
   uint32_t hval;
-  unsigned int cnt;
-  unsigned int idx;
+  unsigned int cnt, idx;
   struct locarhead *head;
   struct namehashent *namehashent;
   unsigned int incr;
   struct locrecent *locrecent;
+  off64_t lastoffset;
+  char *ptr;
+  struct locale_category_data *size_order[__LC_LAST];
+  const size_t pagesz = getpagesize ();
+  int small_mask;
 
   head = ah->addr;
   sumhashtab = (struct sumhashent *) ((char *) ah->addr
 				      + head->sumhash_offset);
 
+  memset (file_offsets, 0, sizeof (file_offsets));
+
+  size_order[LC_ALL] = NULL;
+  for (cnt = 0; cnt < __LC_LAST; ++cnt)
+    if (cnt != LC_ALL)
+      size_order[cnt] = &data[cnt];
+
+  /* Sort the array in ascending order of data size.  */
+  qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
+
+  small_mask = 0;
+  data[LC_ALL].size = 0;
+  for (cnt = 0; cnt < __LC_LAST; ++cnt)
+    if (size_order[cnt] != NULL)
+      {
+	const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
+	if (data[LC_ALL].size + rounded_size > 2 * pagesz)
+	  {
+	    /* This category makes the small-categories block
+	       stop being small, so this is the end of the road.  */
+	    do
+	      size_order[cnt++] = NULL;
+	    while (cnt < __LC_LAST);
+	    break;
+	  }
+	data[LC_ALL].size += rounded_size;
+	small_mask |= 1 << (size_order[cnt] - data);
+      }
+
+  /* Copy the data for all the small categories into the LC_ALL
+     pseudo-category.  */
+
+  data[LC_ALL].addr = alloca (data[LC_ALL].size);
+  memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
+
+  ptr = data[LC_ALL].addr;
+  for (cnt = 0; cnt < __LC_LAST; ++cnt)
+    if (small_mask & (1 << cnt))
+      {
+	memcpy (ptr, data[cnt].addr, data[cnt].size);
+	ptr += (data[cnt].size + 15) & -16;
+      }
+  __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
 
   /* For each locale category data set determine whether the same data
      is already somewhere in the archive.  */
   for (cnt = 0; cnt < __LC_LAST; ++cnt)
-    if (cnt != LC_ALL)
+    if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
       {
-	/* By default signal that we have no data.  */
-	file_offsets[cnt] = 0;
 	++num_new_offsets;
 
 	/* Compute the hash value of the checksum to determine a
@@ -638,8 +753,9 @@ add_locale (struct locarhandle *ah,
     }
 
   /* Add the locale data which is not yet in the archive.  */
-  for (cnt = 0; cnt < __LC_LAST; ++cnt)
-    if (cnt != LC_ALL && file_offsets[cnt] == 0)
+  for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
+    if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
+	&& file_offsets[cnt] == 0)
       {
 	/* The data for this section is not yet available in the
 	   archive.  Append it.  */
@@ -650,6 +766,24 @@ add_locale (struct locarhandle *ah,
 	if (lastpos == (off64_t) -1)
 	  error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
 
+	/* If block of small categories would cross page boundary,
+	   align it unless it immediately follows a large category.  */
+	if (cnt == LC_ALL && lastoffset != lastpos
+	    && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
+		 & -pagesz)
+		> ((data[cnt].size + pagesz - 1) & -pagesz)))
+	  {
+	    size_t sz = pagesz - (lastpos & (pagesz - 1));
+	    char *zeros = alloca (sz);
+
+	    memset (zeros, 0, sz);
+	    if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
+	      error (EXIT_FAILURE, errno,
+		     _("cannot add to locale archive"));
+
+	    lastpos += sz;
+	  }
+
 	/* Align all data to a 16 byte boundary.  */
 	if ((lastpos & 15) != 0)
 	  {
@@ -664,6 +798,7 @@ add_locale (struct locarhandle *ah,
 
 	/* Remember the position.  */
 	file_offsets[cnt] = lastpos;
+	lastoffset = lastpos + data[cnt].size;
 
 	/* Write the data.  */
 	if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
@@ -689,6 +824,14 @@ add_locale (struct locarhandle *ah,
 	++head->sumhash_used;
       }
 
+  lastoffset = file_offsets[LC_ALL];
+  for (cnt = 0; cnt < __LC_LAST; ++cnt)
+    if (small_mask & (1 << cnt))
+      {
+	file_offsets[cnt] = lastoffset;
+	lastoffset += (data[cnt].size + 15) & -16;
+      }
+
   if (namehashent->name_offset == 0)
     {
       /* Add the name string.  */
@@ -730,11 +873,10 @@ add_locale (struct locarhandle *ah,
 
   /* Fill in the table with the locations of the locale data.  */
   for (cnt = 0; cnt < __LC_LAST; ++cnt)
-    if (cnt != LC_ALL)
-      {
-	locrecent->record[cnt].offset = file_offsets[cnt];
-	locrecent->record[cnt].len = data[cnt].size;
-      }
+    {
+      locrecent->record[cnt].offset = file_offsets[cnt];
+      locrecent->record[cnt].len = data[cnt].size;
+    }
 
   return namehashent->locrec_offset;
 }
@@ -1292,7 +1434,14 @@ show_archive_content (int verbose)
 	  locrec = (struct locrecent *) ((char *) ah.addr
 					 + names[cnt].locrec_offset);
 	  for (idx = 0; idx < __LC_LAST; ++idx)
-	    if (idx != LC_ALL)
+	    if (locrec->record[LC_ALL].offset != 0
+		? (idx == LC_ALL
+		   || (locrec->record[idx].offset
+		       < locrec->record[LC_ALL].offset)
+		   || (locrec->record[idx].offset + locrec->record[idx].len
+		       > (locrec->record[LC_ALL].offset
+			  + locrec->record[LC_ALL].len)))
+		: idx != LC_ALL)
 	      {
 		struct dataent *data, dataent;
 
@@ -1319,12 +1468,21 @@ show_archive_content (int verbose)
 		struct dataent *data, dataent;
 
 		dataent.file_offset = locrec->record[idx].offset;
+		if (locrec->record[LC_ALL].offset != 0
+		    && dataent.file_offset >= locrec->record[LC_ALL].offset
+		    && (dataent.file_offset + locrec->record[idx].len
+			<= (locrec->record[LC_ALL].offset
+			    + locrec->record[LC_ALL].len)))
+		  dataent.file_offset = locrec->record[LC_ALL].offset;
+
 		data = (struct dataent *) bsearch (&dataent, files, sumused,
 						   sizeof (struct dataent),
 						   dataentcmp);
-		printf ("%6d %7x %3d ",
+		printf ("%6d %7x %3d%c ",
 			locrec->record[idx].len, locrec->record[idx].offset,
-			data->nlink);
+			data->nlink,
+			dataent.file_offset == locrec->record[LC_ALL].offset
+			? '+' : ' ');
 		for (i = 0; i < 16; i += 4)
 		    printf ("%02x%02x%02x%02x",
 			    data->sum[i], data->sum[i + 1],