Add FxAlignToBoundary<>() template helper function.

Because I nearly botched this trivial calculation in the previous CL.

Change-Id: I7438f9d3476d93b7899c2d7d761234769f53f9e3
Reviewed-on: https://pdfium-review.googlesource.com/43010
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcodec/codec/ccodec_faxmodule.cpp b/core/fxcodec/codec/ccodec_faxmodule.cpp
index 423dfe6..fe9f6a9 100644
--- a/core/fxcodec/codec/ccodec_faxmodule.cpp
+++ b/core/fxcodec/codec/ccodec_faxmodule.cpp
@@ -530,7 +530,7 @@
 
   if (m_bByteAlign && m_bitpos < bitsize) {
     int bitpos0 = m_bitpos;
-    int bitpos1 = (m_bitpos + 7) / 8 * 8;
+    int bitpos1 = FxAlignToBoundary<8>(m_bitpos);
     while (m_bByteAlign && bitpos0 < bitpos1) {
       int bit = m_SrcSpan[bitpos0 / 8] & (1 << (7 - bitpos0 % 8));
       if (bit != 0)
diff --git a/core/fxcodec/codec/ccodec_progressivedecoder.cpp b/core/fxcodec/codec/ccodec_progressivedecoder.cpp
index c2d121d..16408a2 100644
--- a/core/fxcodec/codec/ccodec_progressivedecoder.cpp
+++ b/core/fxcodec/codec/ccodec_progressivedecoder.cpp
@@ -839,7 +839,7 @@
     return m_status;
   }
   GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  m_ScanlineSize = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4;
+  m_ScanlineSize = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
   m_pDecodeBuf.reset(FX_Alloc(uint8_t, m_ScanlineSize));
   memset(m_pDecodeBuf.get(), 0, m_ScanlineSize);
   m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
@@ -971,7 +971,7 @@
   }
   m_SrcFormat = FXCodec_8bppRgb;
   GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  int scanline_size = (m_SrcWidth + 3) / 4 * 4;
+  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth);
   m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
   memset(m_pDecodeBuf.get(), 0, scanline_size);
   m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
@@ -1211,7 +1211,7 @@
     startStatus = pJpegModule->StartScanline(m_pJpegContext.get(), down_scale);
   }
   int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale;
-  scanline_size = (scanline_size * m_SrcComponents + 3) / 4 * 4;
+  scanline_size = FxAlignToBoundary<4>(scanline_size * m_SrcComponents);
   m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
   memset(m_pDecodeBuf.get(), 0, scanline_size);
   m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
@@ -1435,7 +1435,7 @@
     }
   }
   GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  int scanline_size = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4;
+  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
   m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
   memset(m_pDecodeBuf.get(), 0, scanline_size);
   m_WeightHorzOO.Calc(m_sizeX, m_clipBox.Width());
diff --git a/core/fxcodec/jbig2/JBig2_Image.cpp b/core/fxcodec/jbig2/JBig2_Image.cpp
index 1194b1a..a2d9f48 100644
--- a/core/fxcodec/jbig2/JBig2_Image.cpp
+++ b/core/fxcodec/jbig2/JBig2_Image.cpp
@@ -44,7 +44,7 @@
   if (w <= 0 || h <= 0 || w > kMaxImagePixels)
     return;
 
-  int32_t stride_pixels = (w + 31) & ~31;
+  int32_t stride_pixels = FxAlignToBoundary<32>(w);
   if (h > kMaxImagePixels / stride_pixels)
     return;
 
diff --git a/core/fxcrt/cfx_bitstream.cpp b/core/fxcrt/cfx_bitstream.cpp
index 3d3040d..d16fc2f 100644
--- a/core/fxcrt/cfx_bitstream.cpp
+++ b/core/fxcrt/cfx_bitstream.cpp
@@ -8,6 +8,7 @@
 
 #include <limits>
 
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
 
 CFX_BitStream::CFX_BitStream(pdfium::span<const uint8_t> pData)
@@ -18,7 +19,7 @@
 CFX_BitStream::~CFX_BitStream() {}
 
 void CFX_BitStream::ByteAlign() {
-  m_BitPos = (m_BitPos + 7) & ~7;
+  m_BitPos = FxAlignToBoundary<8>(m_BitPos);
 }
 
 uint32_t CFX_BitStream::GetBits(uint32_t nBits) {
diff --git a/core/fxcrt/fx_memory.h b/core/fxcrt/fx_memory.h
index 707e084..b399112 100644
--- a/core/fxcrt/fx_memory.h
+++ b/core/fxcrt/fx_memory.h
@@ -130,6 +130,13 @@
 template <typename T, size_t N>
 char (&ArraySizeHelper(T (&array)[N]))[N];
 
+// Round up to the power-of-two boundary N.
+template <int N, typename T>
+inline T FxAlignToBoundary(T size) {
+  static_assert(N > 0 && (N & (N - 1)) == 0, "Not non-zero power of two");
+  return (size + N - 1) & ~(N - 1);
+}
+
 // Used with std::unique_ptr to FX_Free raw memory.
 struct FxFreeDeleter {
   inline void operator()(void* ptr) const { FX_Free(ptr); }
diff --git a/core/fxcrt/fx_memory_unittest.cpp b/core/fxcrt/fx_memory_unittest.cpp
index 8c57783..eab1111 100644
--- a/core/fxcrt/fx_memory_unittest.cpp
+++ b/core/fxcrt/fx_memory_unittest.cpp
@@ -83,3 +83,26 @@
   EXPECT_FALSE(FXMEM_DefaultRealloc(ptr, kMaxByteAlloc));
   FXMEM_DefaultFree(ptr);
 }
+
+TEST(fxcrt, FXAlign) {
+  static_assert(std::numeric_limits<size_t>::max() % 2 == 1,
+                "numeric limit must be odd for this test");
+
+  size_t s0 = 0;
+  size_t s1 = 1;
+  size_t s2 = 2;
+  size_t sbig = std::numeric_limits<size_t>::max() - 2;
+  EXPECT_EQ(0u, FxAlignToBoundary<2>(s0));
+  EXPECT_EQ(2u, FxAlignToBoundary<2>(s1));
+  EXPECT_EQ(2u, FxAlignToBoundary<2>(s2));
+  EXPECT_EQ(std::numeric_limits<size_t>::max() - 1, FxAlignToBoundary<2>(sbig));
+
+  int i0 = 0;
+  int i511 = 511;
+  int i512 = 512;
+  int ineg = -513;
+  EXPECT_EQ(0, FxAlignToBoundary<512>(i0));
+  EXPECT_EQ(512, FxAlignToBoundary<512>(i511));
+  EXPECT_EQ(512, FxAlignToBoundary<512>(i512));
+  EXPECT_EQ(-512, FxAlignToBoundary<512>(ineg));
+}
diff --git a/core/fxge/dib/cfx_imagestretcher.cpp b/core/fxge/dib/cfx_imagestretcher.cpp
index 809d3b1..ea4dcae 100644
--- a/core/fxge/dib/cfx_imagestretcher.cpp
+++ b/core/fxge/dib/cfx_imagestretcher.cpp
@@ -160,10 +160,11 @@
     return false;
 
   size *= m_DestBPP;
-  m_pScanline.reset(FX_Alloc(uint8_t, (size / 8 + 3) / 4 * 4));
-  if (m_pSource->m_pAlphaMask)
-    m_pMaskScanline.reset(FX_Alloc(uint8_t, (m_ClipRect.Width() + 3) / 4 * 4));
-
+  m_pScanline.reset(FX_Alloc(uint8_t, FxAlignToBoundary<4>(size / 8)));
+  if (m_pSource->m_pAlphaMask) {
+    m_pMaskScanline.reset(
+        FX_Alloc(uint8_t, FxAlignToBoundary<4>(m_ClipRect.Width())));
+  }
   if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) {
     ContinueQuickStretch(nullptr);
     return false;
diff --git a/core/fxge/win32/fx_win32_gdipext.cpp b/core/fxge/win32/fx_win32_gdipext.cpp
index 8d361e7..5778034 100644
--- a/core/fxge/win32/fx_win32_gdipext.cpp
+++ b/core/fxge/win32/fx_win32_gdipext.cpp
@@ -525,7 +525,7 @@
 
   int result_width = pClipRect->Width();
   int result_height = pClipRect->Height();
-  int result_pitch = (result_width + 3) / 4 * 4;
+  int result_pitch = FxAlignToBoundary<4>(result_width);
   auto pStretched = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb))
     return nullptr;
@@ -620,10 +620,10 @@
           pBitmap->StretchTo(dest_width, dest_height, false, &image_clip);
     }
     GpBitmap* bitmap;
-    CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),
-                                        (image_clip.Width() + 3) / 4 * 4,
-                                        PixelFormat8bppIndexed,
-                                        pStretched->GetBuffer(), &bitmap);
+    CallFunc(GdipCreateBitmapFromScan0)(
+        image_clip.Width(), image_clip.Height(),
+        FxAlignToBoundary<4>(image_clip.Width()), PixelFormat8bppIndexed,
+        pStretched->GetBuffer(), &bitmap);
     int a;
     int r;
     int g;
@@ -772,7 +772,7 @@
   CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
   if (!pGraphState->m_DashArray.empty()) {
     float* pDashArray =
-        FX_Alloc(float, (pGraphState->m_DashArray.size() + 1) & ~1);
+        FX_Alloc(float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size()));
     int nCount = 0;
     float on_leftover = 0, off_leftover = 0;
     for (size_t i = 0; i < pGraphState->m_DashArray.size(); i += 2) {