1/* Convert UTF-32 string to UTF-16 string.
2   Copyright (C) 2002, 2006-2007, 2009-2010 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2002.
4
5   This program is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Lesser General Public License as published
7   by the Free Software Foundation; either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18#include <config.h>
19
20/* Specification.  */
21#include "unistr.h"
22
23#define FUNC u32_to_u16
24#define SRC_UNIT uint32_t
25#define DST_UNIT uint16_t
26
27#include <errno.h>
28#include <stdlib.h>
29#include <string.h>
30
31DST_UNIT *
32FUNC (const SRC_UNIT *s, size_t n, DST_UNIT *resultbuf, size_t *lengthp)
33{
34  const SRC_UNIT *s_end = s + n;
35  /* Output string accumulator.  */
36  DST_UNIT *result;
37  size_t allocated;
38  size_t length;
39
40  if (resultbuf != NULL)
41    {
42      result = resultbuf;
43      allocated = *lengthp;
44    }
45  else
46    {
47      result = NULL;
48      allocated = 0;
49    }
50  length = 0;
51  /* Invariants:
52     result is either == resultbuf or == NULL or malloc-allocated.
53     If length > 0, then result != NULL.  */
54
55  while (s < s_end)
56    {
57      ucs4_t uc;
58      int count;
59
60      /* Fetch a Unicode character from the input string.  */
61      uc = *s++;
62      /* No need to call the safe variant u32_mbtouc, because
63         u16_uctomb will verify uc anyway.  */
64
65      /* Store it in the output string.  */
66      count = u16_uctomb (result + length, uc, allocated - length);
67      if (count == -1)
68        {
69          if (!(result == resultbuf || result == NULL))
70            free (result);
71          errno = EILSEQ;
72          return NULL;
73        }
74      if (count == -2)
75        {
76          DST_UNIT *memory;
77
78          allocated = (allocated > 0 ? 2 * allocated : 12);
79          if (length + 2 > allocated)
80            allocated = length + 2;
81          if (result == resultbuf || result == NULL)
82            memory = (DST_UNIT *) malloc (allocated * sizeof (DST_UNIT));
83          else
84            memory =
85              (DST_UNIT *) realloc (result, allocated * sizeof (DST_UNIT));
86
87          if (memory == NULL)
88            {
89              if (!(result == resultbuf || result == NULL))
90                free (result);
91              errno = ENOMEM;
92              return NULL;
93            }
94          if (result == resultbuf && length > 0)
95            memcpy ((char *) memory, (char *) result,
96                    length * sizeof (DST_UNIT));
97          result = memory;
98          count = u16_uctomb (result + length, uc, allocated - length);
99          if (count < 0)
100            abort ();
101        }
102      length += count;
103    }
104
105  if (length == 0)
106    {
107      if (result == NULL)
108        {
109          /* Return a non-NULL value.  NULL means error.  */
110          result = (DST_UNIT *) malloc (1);
111          if (result == NULL)
112            {
113              errno = ENOMEM;
114              return NULL;
115            }
116        }
117    }
118  else if (result != resultbuf && length < allocated)
119    {
120      /* Shrink the allocated memory if possible.  */
121      DST_UNIT *memory;
122
123      memory = (DST_UNIT *) realloc (result, length * sizeof (DST_UNIT));
124      if (memory != NULL)
125        result = memory;
126    }
127
128  *lengthp = length;
129  return result;
130}
131