LCOV - code coverage report
Current view: top level - gcc/diagnostics - file-cache.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 84.9 % 445 378
Test Date: 2025-09-20 13:40:47 Functions: 87.5 % 40 35
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Caching input files for use by diagnostics.
       2                 :             :    Copyright (C) 2004-2025 Free Software Foundation, Inc.
       3                 :             : 
       4                 :             : This file is part of GCC.
       5                 :             : 
       6                 :             : GCC is free software; you can redistribute it and/or modify it under
       7                 :             : the terms of the GNU General Public License as published by the Free
       8                 :             : Software Foundation; either version 3, or (at your option) any later
       9                 :             : version.
      10                 :             : 
      11                 :             : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      12                 :             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13                 :             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14                 :             : for more details.
      15                 :             : 
      16                 :             : You should have received a copy of the GNU General Public License
      17                 :             : along with GCC; see the file COPYING3.  If not see
      18                 :             : <http://www.gnu.org/licenses/>.  */
      19                 :             : 
      20                 :             : #include "config.h"
      21                 :             : #include "system.h"
      22                 :             : #include "coretypes.h"
      23                 :             : #include "cpplib.h"
      24                 :             : #include "diagnostics/file-cache.h"
      25                 :             : #include "diagnostics/dumping.h"
      26                 :             : #include "selftest.h"
      27                 :             : 
      28                 :             : #ifndef HAVE_ICONV
      29                 :             : #define HAVE_ICONV 0
      30                 :             : #endif
      31                 :             : 
      32                 :             : namespace diagnostics {
      33                 :             : 
      34                 :             : /* Input charset configuration.  */
      35                 :       26048 : static const char *default_charset_callback (const char *)
      36                 :             : {
      37                 :       26048 :   return nullptr;
      38                 :             : }
      39                 :             : 
      40                 :             : void
      41                 :      922254 : file_cache::initialize_input_context (diagnostic_input_charset_callback ccb,
      42                 :             :                                       bool should_skip_bom)
      43                 :             : {
      44                 :      922254 :   m_input_context.ccb = (ccb ? ccb : default_charset_callback);
      45                 :      922254 :   m_input_context.should_skip_bom = should_skip_bom;
      46                 :      922254 : }
      47                 :             : 
      48                 :             : /* This is a cache used by get_next_line to store the content of a
      49                 :             :    file to be searched for file lines.  */
      50                 :             : class file_cache_slot
      51                 :             : {
      52                 :             : public:
      53                 :             :   file_cache_slot ();
      54                 :             :   ~file_cache_slot ();
      55                 :             : 
      56                 :             :   void dump (FILE *out, int indent) const;
      57                 :           0 :   void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
      58                 :             : 
      59                 :             :   bool read_line_num (size_t line_num,
      60                 :             :                       char ** line, ssize_t *line_len);
      61                 :             : 
      62                 :             :   /* Accessors.  */
      63                 :    27866102 :   const char *get_file_path () const { return m_file_path; }
      64                 :      330786 :   unsigned get_use_count () const { return m_use_count; }
      65                 :        3489 :   bool missing_trailing_newline_p () const
      66                 :             :   {
      67                 :        3489 :     return m_missing_trailing_newline;
      68                 :             :   }
      69                 :             :   char_span get_full_file_content ();
      70                 :             : 
      71                 :     3189028 :   void inc_use_count () { m_use_count++; }
      72                 :             : 
      73                 :             :   bool create (const file_cache::input_context &in_context,
      74                 :             :                const char *file_path, FILE *fp, unsigned highest_use_count);
      75                 :             :   void evict ();
      76                 :             :   void set_content (const char *buf, size_t sz);
      77                 :             : 
      78                 :      285689 :   static size_t tune (size_t line_record_size_)
      79                 :             :   {
      80                 :      285689 :     size_t ret = line_record_size;
      81                 :      285689 :     line_record_size = line_record_size_;
      82                 :      285689 :     return ret;
      83                 :             :   }
      84                 :             : 
      85                 :             :  private:
      86                 :             :   /* These are information used to store a line boundary.  */
      87                 :             :   class line_info
      88                 :             :   {
      89                 :             :   public:
      90                 :             :     /* The line number.  It starts from 1.  */
      91                 :             :     size_t line_num;
      92                 :             : 
      93                 :             :     /* The position (byte count) of the beginning of the line,
      94                 :             :        relative to the file data pointer.  This starts at zero.  */
      95                 :             :     size_t start_pos;
      96                 :             : 
      97                 :             :     /* The position (byte count) of the last byte of the line.  This
      98                 :             :        normally points to the '\n' character, or to one byte after the
      99                 :             :        last byte of the file, if the file doesn't contain a '\n'
     100                 :             :        character.  */
     101                 :             :     size_t end_pos;
     102                 :             : 
     103                 :  2942686760 :     line_info (size_t l, size_t s, size_t e)
     104                 :  2926353823 :       : line_num (l), start_pos (s), end_pos (e)
     105                 :             :     {}
     106                 :             : 
     107                 :             :     line_info ()
     108                 :             :       :line_num (0), start_pos (0), end_pos (0)
     109                 :             :     {}
     110                 :             : 
     111                 :       52294 :     static bool less_than(const line_info &a, const line_info &b)
     112                 :             :     {
     113                 :       52294 :       return a.line_num < b.line_num;
     114                 :             :     }
     115                 :             :   };
     116                 :             : 
     117                 :             :   bool needs_read_p () const;
     118                 :             :   bool needs_grow_p () const;
     119                 :             :   void maybe_grow ();
     120                 :             :   bool read_data ();
     121                 :             :   bool maybe_read_data ();
     122                 :             :   bool get_next_line (char **line, ssize_t *line_len);
     123                 :             :   bool read_next_line (char ** line, ssize_t *line_len);
     124                 :             :   bool goto_next_line ();
     125                 :             : 
     126                 :             :   static const size_t buffer_size = 4 * 1024;
     127                 :             :   static size_t line_record_size;
     128                 :             :   static size_t recent_cached_lines_shift;
     129                 :             : 
     130                 :             :   /* The number of time this file has been accessed.  This is used
     131                 :             :      to designate which file cache to evict from the cache
     132                 :             :      array.  */
     133                 :             :   unsigned m_use_count;
     134                 :             : 
     135                 :             :   /* The file_path is the key for identifying a particular file in
     136                 :             :      the cache.  This copy is owned by the slot.  */
     137                 :             :   char *m_file_path;
     138                 :             : 
     139                 :             :   FILE *m_fp;
     140                 :             : 
     141                 :             :   /* True when an read error happened.  */
     142                 :             :   bool m_error;
     143                 :             : 
     144                 :             :   /* This points to the content of the file that we've read so
     145                 :             :      far.  */
     146                 :             :   char *m_data;
     147                 :             : 
     148                 :             :   /* The allocated buffer to be freed may start a little earlier than DATA,
     149                 :             :      e.g. if a UTF8 BOM was skipped at the beginning.  */
     150                 :             :   int m_alloc_offset;
     151                 :             : 
     152                 :             :   /*  The size of the DATA array above.*/
     153                 :             :   size_t m_size;
     154                 :             : 
     155                 :             :   /* The number of bytes read from the underlying file so far.  This
     156                 :             :      must be less (or equal) than SIZE above.  */
     157                 :             :   size_t m_nb_read;
     158                 :             : 
     159                 :             :   /* The index of the beginning of the current line.  */
     160                 :             :   size_t m_line_start_idx;
     161                 :             : 
     162                 :             :   /* The number of the previous line read.  This starts at 1.  Zero
     163                 :             :      means we've read no line so far.  */
     164                 :             :   size_t m_line_num;
     165                 :             : 
     166                 :             :   /* Could this file be missing a trailing newline on its final line?
     167                 :             :      Initially true (to cope with empty files), set to true/false
     168                 :             :      as each line is read.  */
     169                 :             :   bool m_missing_trailing_newline;
     170                 :             : 
     171                 :             :   /* This is a record of the beginning and end of the lines we've seen
     172                 :             :      while reading the file.  This is useful to avoid walking the data
     173                 :             :      from the beginning when we are asked to read a line that is
     174                 :             :      before LINE_START_IDX above.  When the lines exceed line_record_size
     175                 :             :      this is scaled down dynamically, with the line_info becoming anchors.  */
     176                 :             :   vec<line_info, va_heap> m_line_record;
     177                 :             : 
     178                 :             :   /* A cache of the recently seen lines. This is maintained as a ring
     179                 :             :      buffer. */
     180                 :             :   vec<line_info, va_heap> m_line_recent;
     181                 :             : 
     182                 :             :   /* First and last valid entry in m_line_recent.  */
     183                 :             :   size_t m_line_recent_last, m_line_recent_first;
     184                 :             : 
     185                 :      249244 :   void offset_buffer (int offset)
     186                 :             :   {
     187                 :      249244 :     gcc_assert (offset < 0 ? m_alloc_offset + offset >= 0
     188                 :             :                 : (size_t) offset <= m_size);
     189                 :      249244 :     gcc_assert (m_data);
     190                 :      249244 :     m_alloc_offset += offset;
     191                 :      249244 :     m_data += offset;
     192                 :      249244 :     m_size -= offset;
     193                 :      249244 :   }
     194                 :             : 
     195                 :             : };
     196                 :             : 
     197                 :             : size_t file_cache_slot::line_record_size = 0;
     198                 :             : size_t file_cache_slot::recent_cached_lines_shift = 8;
     199                 :             : 
     200                 :             : /* Tune file_cache.  */
     201                 :             : void
     202                 :      285689 : file_cache::tune (size_t num_file_slots, size_t lines)
     203                 :             : {
     204                 :      285689 :   if (file_cache_slot::tune (lines) != lines
     205                 :      285689 :       || m_num_file_slots != num_file_slots)
     206                 :             :     {
     207                 :          17 :       delete[] m_file_slots;
     208                 :         181 :       m_file_slots = new file_cache_slot[num_file_slots];
     209                 :             :     }
     210                 :      285689 :   m_num_file_slots = num_file_slots;
     211                 :      285689 : }
     212                 :             : 
     213                 :             : static const char *
     214                 :             : find_end_of_line (const char *s, size_t len);
     215                 :             : 
     216                 :             : /* Lookup the cache used for the content of a given file accessed by
     217                 :             :    caret diagnostic.  Return the found cached file, or NULL if no
     218                 :             :    cached file was found.  */
     219                 :             : 
     220                 :             : file_cache_slot *
     221                 :     1712430 : file_cache::lookup_file (const char *file_path)
     222                 :             : {
     223                 :     1712430 :   gcc_assert (file_path);
     224                 :             : 
     225                 :             :   /* This will contain the found cached file.  */
     226                 :             :   file_cache_slot *r = NULL;
     227                 :    29111474 :   for (unsigned i = 0; i < m_num_file_slots; ++i)
     228                 :             :     {
     229                 :    27399044 :       file_cache_slot *c = &m_file_slots[i];
     230                 :    27399044 :       if (c->get_file_path () && !strcmp (c->get_file_path (), file_path))
     231                 :             :         {
     232                 :     1594514 :           c->inc_use_count ();
     233                 :     1594514 :           r = c;
     234                 :             :         }
     235                 :             :     }
     236                 :             : 
     237                 :     1712430 :   if (r)
     238                 :     1594514 :     r->inc_use_count ();
     239                 :             : 
     240                 :     1712430 :   return r;
     241                 :             : }
     242                 :             : 
     243                 :             : /* Purge any mention of FILENAME from the cache of files used for
     244                 :             :    printing source code.  For use in selftests when working
     245                 :             :    with tempfiles.  */
     246                 :             : 
     247                 :             : void
     248                 :         576 : file_cache::forcibly_evict_file (const char *file_path)
     249                 :             : {
     250                 :         576 :   gcc_assert (file_path);
     251                 :             : 
     252                 :         576 :   file_cache_slot *r = lookup_file (file_path);
     253                 :         576 :   if (!r)
     254                 :             :     /* Not found.  */
     255                 :             :     return;
     256                 :             : 
     257                 :           0 :   r->evict ();
     258                 :             : }
     259                 :             : 
     260                 :             : /* Determine if FILE_PATH missing a trailing newline on its final line.
     261                 :             :    Only valid to call once all of the file has been loaded, by
     262                 :             :    requesting a line number beyond the end of the file.  */
     263                 :             : 
     264                 :             : bool
     265                 :        3489 : file_cache::missing_trailing_newline_p (const char *file_path)
     266                 :             : {
     267                 :        3489 :   gcc_assert (file_path);
     268                 :             : 
     269                 :        3489 :   file_cache_slot *r = lookup_or_add_file (file_path);
     270                 :        3489 :   return r->missing_trailing_newline_p ();
     271                 :             : }
     272                 :             : 
     273                 :             : void
     274                 :           4 : file_cache::add_buffered_content (const char *file_path,
     275                 :             :                                   const char *buffer,
     276                 :             :                                   size_t sz)
     277                 :             : {
     278                 :           4 :   gcc_assert (file_path);
     279                 :             : 
     280                 :           4 :   file_cache_slot *r = lookup_file (file_path);
     281                 :           4 :   if (!r)
     282                 :             :     {
     283                 :           4 :       unsigned highest_use_count = 0;
     284                 :           4 :       r = evicted_cache_tab_entry (&highest_use_count);
     285                 :           4 :       if (!r->create (m_input_context, file_path, nullptr, highest_use_count))
     286                 :           0 :         return;
     287                 :             :     }
     288                 :             : 
     289                 :           4 :   r->set_content (buffer, sz);
     290                 :             : }
     291                 :             : 
     292                 :             : void
     293                 :           0 : file_cache_slot::evict ()
     294                 :             : {
     295                 :           0 :   free (m_file_path);
     296                 :           0 :   m_file_path = NULL;
     297                 :           0 :   if (m_fp)
     298                 :           0 :     fclose (m_fp);
     299                 :           0 :   m_error = false;
     300                 :           0 :   m_fp = NULL;
     301                 :           0 :   m_nb_read = 0;
     302                 :           0 :   m_line_start_idx = 0;
     303                 :           0 :   m_line_num = 0;
     304                 :           0 :   m_line_record.truncate (0);
     305                 :           0 :   m_line_recent_first = 0;
     306                 :           0 :   m_line_recent_last = 0;
     307                 :           0 :   m_use_count = 0;
     308                 :           0 :   m_missing_trailing_newline = true;
     309                 :           0 : }
     310                 :             : 
     311                 :             : /* Return the file cache that has been less used, recently, or the
     312                 :             :    first empty one.  If HIGHEST_USE_COUNT is non-null,
     313                 :             :    *HIGHEST_USE_COUNT is set to the highest use count of the entries
     314                 :             :    in the cache table.  */
     315                 :             : 
     316                 :             : file_cache_slot*
     317                 :       86997 : file_cache::evicted_cache_tab_entry (unsigned *highest_use_count)
     318                 :             : {
     319                 :       86997 :   file_cache_slot *to_evict = &m_file_slots[0];
     320                 :       86997 :   unsigned huc = to_evict->get_use_count ();
     321                 :      250029 :   for (unsigned i = 1; i < m_num_file_slots; ++i)
     322                 :             :     {
     323                 :      243789 :       file_cache_slot *c = &m_file_slots[i];
     324                 :      243789 :       bool c_is_empty = (c->get_file_path () == NULL);
     325                 :             : 
     326                 :      243789 :       if (c->get_use_count () < to_evict->get_use_count ()
     327                 :      243789 :           || (to_evict->get_file_path () && c_is_empty))
     328                 :             :         /* We evict C because it's either an entry with a lower use
     329                 :             :            count or one that is empty.  */
     330                 :             :         to_evict = c;
     331                 :             : 
     332                 :      243789 :       if (huc < c->get_use_count ())
     333                 :             :         huc = c->get_use_count ();
     334                 :             : 
     335                 :      243789 :       if (c_is_empty)
     336                 :             :         /* We've reached the end of the cache; subsequent elements are
     337                 :             :            all empty.  */
     338                 :             :         break;
     339                 :             :     }
     340                 :             : 
     341                 :       86997 :   if (highest_use_count)
     342                 :       86997 :     *highest_use_count = huc;
     343                 :             : 
     344                 :       86997 :   return to_evict;
     345                 :             : }
     346                 :             : 
     347                 :             : /* Create the cache used for the content of a given file to be
     348                 :             :    accessed by caret diagnostic.  This cache is added to an array of
     349                 :             :    cache and can be retrieved by lookup_file_in_cache_tab.  This
     350                 :             :    function returns the created cache.  Note that only the last
     351                 :             :    m_num_file_slots files are cached.
     352                 :             : 
     353                 :             :    This can return nullptr if the FILE_PATH can't be opened for
     354                 :             :    reading, or if the content can't be converted to the input_charset.  */
     355                 :             : 
     356                 :             : file_cache_slot*
     357                 :      117336 : file_cache::add_file (const char *file_path)
     358                 :             : {
     359                 :             : 
     360                 :      117336 :   FILE *fp = fopen (file_path, "r");
     361                 :      117336 :   if (fp == NULL)
     362                 :             :     return NULL;
     363                 :             : 
     364                 :       86993 :   unsigned highest_use_count = 0;
     365                 :       86993 :   file_cache_slot *r = evicted_cache_tab_entry (&highest_use_count);
     366                 :       86993 :   if (!r->create (m_input_context, file_path, fp, highest_use_count))
     367                 :             :     return NULL;
     368                 :             :   return r;
     369                 :             : }
     370                 :             : 
     371                 :             : /* Get a borrowed char_span to the full content of this file
     372                 :             :    as decoded according to the input charset, encoded as UTF-8.  */
     373                 :             : 
     374                 :             : char_span
     375                 :         225 : file_cache_slot::get_full_file_content ()
     376                 :             : {
     377                 :         225 :   char *line;
     378                 :         225 :   ssize_t line_len;
     379                 :        1805 :   while (get_next_line (&line, &line_len))
     380                 :             :     {
     381                 :             :     }
     382                 :         225 :   return char_span (m_data, m_nb_read);
     383                 :             : }
     384                 :             : 
     385                 :             : /* Populate this slot for use on FILE_PATH and FP, dropping any
     386                 :             :    existing cached content within it.  */
     387                 :             : 
     388                 :             : bool
     389                 :       86997 : file_cache_slot::create (const file_cache::input_context &in_context,
     390                 :             :                          const char *file_path, FILE *fp,
     391                 :             :                          unsigned highest_use_count)
     392                 :             : {
     393                 :       86997 :   m_file_path = file_path ? xstrdup (file_path) : nullptr;
     394                 :       86997 :   if (m_fp)
     395                 :        6240 :     fclose (m_fp);
     396                 :       86997 :   m_error = false;
     397                 :       86997 :   m_fp = fp;
     398                 :       86997 :   if (m_alloc_offset)
     399                 :           0 :     offset_buffer (-m_alloc_offset);
     400                 :       86997 :   m_nb_read = 0;
     401                 :       86997 :   m_line_start_idx = 0;
     402                 :       86997 :   m_line_num = 0;
     403                 :       86997 :   m_line_recent_first = 0;
     404                 :       86997 :   m_line_recent_last = 0;
     405                 :       86997 :   m_line_record.truncate (0);
     406                 :             :   /* Ensure that this cache entry doesn't get evicted next time
     407                 :             :      add_file_to_cache_tab is called.  */
     408                 :       86997 :   m_use_count = ++highest_use_count;
     409                 :       86997 :   m_missing_trailing_newline = true;
     410                 :             : 
     411                 :             : 
     412                 :             :   /* Check the input configuration to determine if we need to do any
     413                 :             :      transformations, such as charset conversion or BOM skipping.  */
     414                 :       86997 :   if (const char *input_charset = in_context.ccb (file_path))
     415                 :             :     {
     416                 :             :       /* Need a full-blown conversion of the input charset.  */
     417                 :           5 :       fclose (m_fp);
     418                 :           5 :       m_fp = NULL;
     419                 :           5 :       const cpp_converted_source cs
     420                 :           5 :         = cpp_get_converted_source (file_path, input_charset);
     421                 :           5 :       if (!cs.data)
     422                 :           0 :         return false;
     423                 :           5 :       if (m_data)
     424                 :           0 :         XDELETEVEC (m_data);
     425                 :           5 :       m_data = cs.data;
     426                 :           5 :       m_nb_read = m_size = cs.len;
     427                 :           5 :       m_alloc_offset = cs.data - cs.to_free;
     428                 :             :     }
     429                 :       86992 :   else if (in_context.should_skip_bom)
     430                 :             :     {
     431                 :       61509 :       if (read_data ())
     432                 :             :         {
     433                 :       61509 :           const int offset = cpp_check_utf8_bom (m_data, m_nb_read);
     434                 :       61509 :           offset_buffer (offset);
     435                 :       61509 :           m_nb_read -= offset;
     436                 :             :         }
     437                 :             :     }
     438                 :             : 
     439                 :             :   return true;
     440                 :             : }
     441                 :             : 
     442                 :             : void
     443                 :           4 : file_cache_slot::set_content (const char *buf, size_t sz)
     444                 :             : {
     445                 :           4 :   m_data = (char *)xmalloc (sz);
     446                 :           4 :   memcpy (m_data, buf, sz);
     447                 :           4 :   m_nb_read = m_size = sz;
     448                 :           4 :   m_alloc_offset = 0;
     449                 :             : 
     450                 :           4 :   if (m_fp)
     451                 :             :     {
     452                 :           0 :       fclose (m_fp);
     453                 :           0 :       m_fp = nullptr;
     454                 :             :     }
     455                 :           4 : }
     456                 :             : 
     457                 :             : /* file_cache's ctor.  */
     458                 :             : 
     459                 :      714148 : file_cache::file_cache ()
     460                 :    12140516 : : m_num_file_slots (16), m_file_slots (new file_cache_slot[m_num_file_slots])
     461                 :             : {
     462                 :      714148 :   initialize_input_context (nullptr, false);
     463                 :      714148 : }
     464                 :             : 
     465                 :             : /* file_cache's dtor.  */
     466                 :             : 
     467                 :      309331 : file_cache::~file_cache ()
     468                 :             : {
     469                 :     5258791 :   delete[] m_file_slots;
     470                 :      309331 : }
     471                 :             : 
     472                 :             : void
     473                 :           0 : file_cache::dump (FILE *out, int indent) const
     474                 :             : {
     475                 :           0 :   for (size_t i = 0; i < m_num_file_slots; ++i)
     476                 :             :     {
     477                 :           0 :       dumping::emit_indent (out, indent);
     478                 :           0 :       fprintf (out, "slot[%i]:\n", (int)i);
     479                 :           0 :       m_file_slots[i].dump (out, indent + 2);
     480                 :             :     }
     481                 :           0 : }
     482                 :             : 
     483                 :             : void
     484                 :           0 : file_cache::dump () const
     485                 :             : {
     486                 :           0 :   dump (stderr, 0);
     487                 :           0 : }
     488                 :             : 
     489                 :             : /* Lookup the cache used for the content of a given file accessed by
     490                 :             :    caret diagnostic.  If no cached file was found, create a new cache
     491                 :             :    for this file, add it to the array of cached file and return
     492                 :             :    it.
     493                 :             : 
     494                 :             :    This can return nullptr on a cache miss if FILE_PATH can't be opened for
     495                 :             :    reading, or if the content can't be converted to the input_charset.  */
     496                 :             : 
     497                 :             : file_cache_slot*
     498                 :     1711850 : file_cache::lookup_or_add_file (const char *file_path)
     499                 :             : {
     500                 :     1711850 :   file_cache_slot *r = lookup_file (file_path);
     501                 :     1711850 :   if (r == NULL)
     502                 :      117336 :     r = add_file (file_path);
     503                 :     1711850 :   return r;
     504                 :             : }
     505                 :             : 
     506                 :             : /* Default constructor for a cache of file used by caret
     507                 :             :    diagnostic.  */
     508                 :             : 
     509                 :    11426548 : file_cache_slot::file_cache_slot ()
     510                 :    11426548 : : m_use_count (0), m_file_path (NULL), m_fp (NULL), m_error (false), m_data (0),
     511                 :    11426548 :   m_alloc_offset (0), m_size (0), m_nb_read (0), m_line_start_idx (0),
     512                 :    11426548 :   m_line_num (0), m_missing_trailing_newline (true),
     513                 :    11426548 :   m_line_recent_last (0), m_line_recent_first (0)
     514                 :             : {
     515                 :    11426548 :   m_line_record.create (0);
     516                 :    11426548 :   m_line_recent.create (1U << recent_cached_lines_shift);
     517                 :  2936622836 :   for (int i = 0; i < 1 << recent_cached_lines_shift; i++)
     518                 :  2925196288 :     m_line_recent.quick_push (file_cache_slot::line_info (0, 0, 0));
     519                 :    11426548 : }
     520                 :             : 
     521                 :             : /* Destructor for a cache of file used by caret diagnostic.  */
     522                 :             : 
     523                 :     4949476 : file_cache_slot::~file_cache_slot ()
     524                 :             : {
     525                 :     4949476 :   free (m_file_path);
     526                 :     4949476 :   if (m_fp)
     527                 :             :     {
     528                 :       79204 :       fclose (m_fp);
     529                 :       79204 :       m_fp = NULL;
     530                 :             :     }
     531                 :     4949476 :   if (m_data)
     532                 :             :     {
     533                 :       79213 :       offset_buffer (-m_alloc_offset);
     534                 :       79213 :       XDELETEVEC (m_data);
     535                 :       79213 :       m_data = 0;
     536                 :             :     }
     537                 :     4949476 :   m_line_record.release ();
     538                 :     4949476 :   m_line_recent.release ();
     539                 :     4949476 : }
     540                 :             : 
     541                 :             : void
     542                 :           0 : file_cache_slot::dump (FILE *out, int indent) const
     543                 :             : {
     544                 :           0 :   if (!m_file_path)
     545                 :             :     {
     546                 :           0 :       dumping::emit_indent (out, indent);
     547                 :           0 :       fprintf (out, "(unused)\n");
     548                 :           0 :       return;
     549                 :             :     }
     550                 :           0 :   dumping::emit_string_field (out, indent, "file_path", m_file_path);
     551                 :           0 :   {
     552                 :           0 :     dumping::emit_indent (out, indent);
     553                 :           0 :     fprintf (out, "fp: %p\n", (void *)m_fp);
     554                 :             :   }
     555                 :           0 :   dumping::emit_bool_field (out, indent, "needs_read_p", needs_read_p ());
     556                 :           0 :   dumping::emit_bool_field (out, indent, "needs_grow_p", needs_grow_p ());
     557                 :           0 :   dumping::emit_unsigned_field (out, indent, "use_count", m_use_count);
     558                 :           0 :   dumping::emit_size_t_field (out, indent, "size", m_size);
     559                 :           0 :   dumping::emit_size_t_field (out, indent, "nb_read", m_nb_read);
     560                 :           0 :   dumping::emit_size_t_field (out, indent, "start_line_idx", m_line_start_idx);
     561                 :           0 :   dumping::emit_size_t_field (out, indent, "line_num", m_line_num);
     562                 :           0 :   dumping::emit_bool_field (out, indent, "missing_trailing_newline",
     563                 :           0 :                             m_missing_trailing_newline);
     564                 :           0 :   {
     565                 :           0 :     dumping::emit_indent (out, indent);
     566                 :           0 :     fprintf (out, "line records (%i):\n", m_line_record.length ());
     567                 :             :   }
     568                 :           0 :   int idx = 0;
     569                 :           0 :   for (auto &line : m_line_record)
     570                 :             :     {
     571                 :           0 :       dumping::emit_indent (out, indent);
     572                 :           0 :       fprintf (out, ("[%i]:"
     573                 :             :                      " line " HOST_SIZE_T_PRINT_DEC ":"
     574                 :             :                      " byte offsets: " HOST_SIZE_T_PRINT_DEC
     575                 :             :                      "-" HOST_SIZE_T_PRINT_DEC "\n"),
     576                 :           0 :                idx++, line.line_num, line.start_pos, line.end_pos);
     577                 :             :     }
     578                 :             : }
     579                 :             : 
     580                 :             : /* Returns TRUE iff the cache would need to be filled with data coming
     581                 :             :    from the file.  That is, either the cache is empty or full or the
     582                 :             :    current line is empty.  Note that if the cache is full, it would
     583                 :             :    need to be extended and filled again.  */
     584                 :             : 
     585                 :             : bool
     586                 :    27593249 : file_cache_slot::needs_read_p () const
     587                 :             : {
     588                 :    27593249 :   return m_fp && (m_nb_read == 0
     589                 :    27567646 :           || m_nb_read == m_size
     590                 :    27513385 :           || (m_line_start_idx >= m_nb_read - 1));
     591                 :             : }
     592                 :             : 
     593                 :             : /*  Return TRUE iff the cache is full and thus needs to be
     594                 :             :     extended.  */
     595                 :             : 
     596                 :             : bool
     597                 :      141249 : file_cache_slot::needs_grow_p () const
     598                 :             : {
     599                 :      141249 :   return m_nb_read == m_size;
     600                 :             : }
     601                 :             : 
     602                 :             : /* Grow the cache if it needs to be extended.  */
     603                 :             : 
     604                 :             : void
     605                 :      141249 : file_cache_slot::maybe_grow ()
     606                 :             : {
     607                 :      141249 :   if (!needs_grow_p ())
     608                 :             :     return;
     609                 :             : 
     610                 :      135009 :   if (!m_data)
     611                 :             :     {
     612                 :       80748 :       gcc_assert (m_size == 0 && m_alloc_offset == 0);
     613                 :       80748 :       m_size = buffer_size;
     614                 :       80748 :       m_data = XNEWVEC (char, m_size);
     615                 :             :     }
     616                 :             :   else
     617                 :             :     {
     618                 :       54261 :       const int offset = m_alloc_offset;
     619                 :       54261 :       offset_buffer (-offset);
     620                 :       54261 :       m_size *= 2;
     621                 :       54261 :       m_data = XRESIZEVEC (char, m_data, m_size);
     622                 :       54261 :       offset_buffer (offset);
     623                 :             :     }
     624                 :             : }
     625                 :             : 
     626                 :             : /*  Read more data into the cache.  Extends the cache if need be.
     627                 :             :     Returns TRUE iff new data could be read.  */
     628                 :             : 
     629                 :             : bool
     630                 :      146048 : file_cache_slot::read_data ()
     631                 :             : {
     632                 :      146048 :   if (feof (m_fp) || ferror (m_fp))
     633                 :        4799 :     return false;
     634                 :             : 
     635                 :      141249 :   maybe_grow ();
     636                 :             : 
     637                 :      141249 :   char * from = m_data + m_nb_read;
     638                 :      141249 :   size_t to_read = m_size - m_nb_read;
     639                 :      141249 :   size_t nb_read = fread (from, 1, to_read, m_fp);
     640                 :             : 
     641                 :      141249 :   if (ferror (m_fp))
     642                 :             :     {
     643                 :           0 :       m_error = true;
     644                 :           0 :       return false;
     645                 :             :     }
     646                 :             : 
     647                 :      141249 :   m_nb_read += nb_read;
     648                 :      141249 :   return !!nb_read;
     649                 :             : }
     650                 :             : 
     651                 :             : /* Read new data iff the cache needs to be filled with more data
     652                 :             :    coming from the file FP.  Return TRUE iff the cache was filled with
     653                 :             :    mode data.  */
     654                 :             : 
     655                 :             : bool
     656                 :    27593249 : file_cache_slot::maybe_read_data ()
     657                 :             : {
     658                 :    27593249 :   if (!needs_read_p ())
     659                 :             :     return false;
     660                 :       84539 :   return read_data ();
     661                 :             : }
     662                 :             : 
     663                 :             : /* Helper function for file_cache_slot::get_next_line (), to find the end of
     664                 :             :    the next line.  Returns with the memchr convention, i.e. nullptr if a line
     665                 :             :    terminator was not found.  We need to determine line endings in the same
     666                 :             :    manner that libcpp does: any of \n, \r\n, or \r is a line ending.  */
     667                 :             : 
     668                 :             : static const char *
     669                 :    27588375 : find_end_of_line (const char *s, size_t len)
     670                 :             : {
     671                 :  1011556595 :   for (const auto end = s + len; s != end; ++s)
     672                 :             :     {
     673                 :  1011549301 :       if (*s == '\n')
     674                 :             :         return s;
     675                 :   983968873 :       if (*s == '\r')
     676                 :             :         {
     677                 :         653 :           const auto next = s + 1;
     678                 :         653 :           if (next == end)
     679                 :             :             {
     680                 :             :               /* Don't find the line ending if \r is the very last character
     681                 :             :                  in the buffer; we do not know if it's the end of the file or
     682                 :             :                  just the end of what has been read so far, and we wouldn't
     683                 :             :                  want to break in the middle of what's actually a \r\n
     684                 :             :                  sequence.  Instead, we will handle the case of a file ending
     685                 :             :                  in a \r later.  */
     686                 :             :               break;
     687                 :             :             }
     688                 :         653 :           return (*next == '\n' ? next : s);
     689                 :             :         }
     690                 :             :     }
     691                 :             :   return nullptr;
     692                 :             : }
     693                 :             : 
     694                 :             : /* Read a new line from file FP, using C as a cache for the data
     695                 :             :    coming from the file.  Upon successful completion, *LINE is set to
     696                 :             :    the beginning of the line found.  *LINE points directly in the
     697                 :             :    line cache and is only valid until the next call of get_next_line.
     698                 :             :    *LINE_LEN is set to the length of the line.  Note that the line
     699                 :             :    does not contain any terminal delimiter.  This function returns
     700                 :             :    true if some data was read or process from the cache, false
     701                 :             :    otherwise.  Note that subsequent calls to get_next_line might
     702                 :             :    make the content of *LINE invalid.  */
     703                 :             : 
     704                 :             : bool
     705                 :    27585955 : file_cache_slot::get_next_line (char **line, ssize_t *line_len)
     706                 :             : {
     707                 :             :   /* Fill the cache with data to process.  */
     708                 :    27585955 :   maybe_read_data ();
     709                 :             : 
     710                 :    27585955 :   size_t remaining_size = m_nb_read - m_line_start_idx;
     711                 :    27585955 :   if (remaining_size == 0)
     712                 :             :     /* There is no more data to process.  */
     713                 :             :     return false;
     714                 :             : 
     715                 :    27581153 :   const char *line_start = m_data + m_line_start_idx;
     716                 :             : 
     717                 :    27581153 :   const char *next_line_start = NULL;
     718                 :    27581153 :   size_t len = 0;
     719                 :    27581153 :   const char *line_end = find_end_of_line (line_start, remaining_size);
     720                 :    27581153 :   if (line_end == NULL)
     721                 :             :     {
     722                 :             :       /* We haven't found an end-of-line delimiter in the cache.
     723                 :             :          Fill the cache with more data from the file and look again.  */
     724                 :        7294 :       while (maybe_read_data ())
     725                 :             :         {
     726                 :        7222 :           line_start = m_data + m_line_start_idx;
     727                 :        7222 :           remaining_size = m_nb_read - m_line_start_idx;
     728                 :        7222 :           line_end = find_end_of_line (line_start, remaining_size);
     729                 :        7222 :           if (line_end != NULL)
     730                 :             :             {
     731                 :        3622 :               next_line_start = line_end + 1;
     732                 :        3622 :               break;
     733                 :             :             }
     734                 :             :         }
     735                 :        3694 :       if (line_end == NULL)
     736                 :             :         {
     737                 :             :           /* We've loaded all the file into the cache and still no
     738                 :             :              terminator.  Let's say the line ends up at one byte past the
     739                 :             :              end of the file.  This is to stay consistent with the case
     740                 :             :              of when the line ends up with a terminator and line_end points to
     741                 :             :              that.  That consistency is useful below in the len calculation.
     742                 :             : 
     743                 :             :              If the file ends in a \r, we didn't identify it as a line
     744                 :             :              terminator above, so do that now instead.  */
     745                 :          72 :           line_end = m_data + m_nb_read;
     746                 :          72 :           if (m_nb_read && line_end[-1] == '\r')
     747                 :             :             {
     748                 :           0 :               --line_end;
     749                 :           0 :               m_missing_trailing_newline = false;
     750                 :             :             }
     751                 :             :           else
     752                 :          72 :             m_missing_trailing_newline = true;
     753                 :             :         }
     754                 :             :       else
     755                 :        3622 :         m_missing_trailing_newline = false;
     756                 :             :     }
     757                 :             :   else
     758                 :             :     {
     759                 :    27577459 :       next_line_start = line_end + 1;
     760                 :    27577459 :       m_missing_trailing_newline = false;
     761                 :             :     }
     762                 :             : 
     763                 :    27581153 :   if (m_error)
     764                 :             :     return false;
     765                 :             : 
     766                 :             :   /* At this point, we've found the end of the of line.  It either points to
     767                 :             :      the line terminator or to one byte after the last byte of the file.  */
     768                 :    27581153 :   gcc_assert (line_end != NULL);
     769                 :             : 
     770                 :    27581153 :   len = line_end - line_start;
     771                 :             : 
     772                 :    27581153 :   if (m_line_start_idx < m_nb_read)
     773                 :    27581153 :     *line = const_cast<char *> (line_start);
     774                 :             : 
     775                 :    27581153 :   ++m_line_num;
     776                 :             : 
     777                 :             :   /* Now update our line record so that re-reading lines from the
     778                 :             :      before m_line_start_idx is faster.  */
     779                 :    27581153 :   size_t rlen = m_line_record.length ();
     780                 :             :   /* Only update when beyond the previously cached region.  */
     781                 :    24661390 :   if (rlen == 0 || m_line_record[rlen - 1].line_num < m_line_num)
     782                 :             :     {
     783                 :    17038210 :       size_t spacing
     784                 :             :         = (rlen >= 2
     785                 :    17038210 :            ? (m_line_record[rlen - 1].line_num
     786                 :    11226460 :               - m_line_record[rlen - 2].line_num) : 1);
     787                 :    17038210 :       size_t delta
     788                 :    17038210 :         = rlen >= 1 ? m_line_num - m_line_record[rlen - 1].line_num : 1;
     789                 :             : 
     790                 :    17038210 :       size_t max_size = line_record_size;
     791                 :             :       /* One anchor per hundred input lines.  */
     792                 :    17038210 :       if (max_size == 0)
     793                 :    17038210 :         max_size = m_line_num / 100;
     794                 :             : 
     795                 :             :       /* If we're too far beyond drop half of the lines to rebalance.  */
     796                 :    17038210 :       if (rlen == max_size && delta >= spacing * 2)
     797                 :             :         {
     798                 :             :           size_t j = 0;
     799                 :     1075520 :           for (size_t i = 1; i < rlen; i += 2)
     800                 :       71437 :             m_line_record[j++] = m_line_record[i];
     801                 :     1004083 :           m_line_record.truncate (j);
     802                 :     1004083 :           rlen = j;
     803                 :     1004083 :           spacing *= 2;
     804                 :             :         }
     805                 :             : 
     806                 :    17038210 :       if (rlen < max_size && delta >= spacing)
     807                 :             :         {
     808                 :     1123541 :           file_cache_slot::line_info li (m_line_num, m_line_start_idx,
     809                 :     1123541 :                                          line_end - m_data);
     810                 :     1123541 :           m_line_record.safe_push (li);
     811                 :             :         }
     812                 :             :     }
     813                 :             : 
     814                 :             :   /* Cache recent tail lines separately for fast access. This assumes
     815                 :             :      most accesses do not skip backwards.  */
     816                 :    27581153 :   if (m_line_recent_last == m_line_recent_first
     817                 :    27581153 :       || m_line_recent[m_line_recent_last].line_num == m_line_num - 1)
     818                 :             :     {
     819                 :    16332937 :       size_t mask = ((size_t) 1 << recent_cached_lines_shift) - 1;
     820                 :    16332937 :       m_line_recent_last = (m_line_recent_last + 1) & mask;
     821                 :    16332937 :       if (m_line_recent_last == m_line_recent_first)
     822                 :    10138989 :         m_line_recent_first = (m_line_recent_first + 1) & mask;
     823                 :    32665874 :       m_line_recent[m_line_recent_last]
     824                 :    16332937 :         = file_cache_slot::line_info (m_line_num, m_line_start_idx,
     825                 :    16332937 :                                       line_end - m_data);
     826                 :             :     }
     827                 :             : 
     828                 :             :   /* Update m_line_start_idx so that it points to the next line to be
     829                 :             :      read.  */
     830                 :    27581153 :   if (next_line_start)
     831                 :    27581081 :     m_line_start_idx = next_line_start - m_data;
     832                 :             :   else
     833                 :             :     /* We didn't find any terminal '\n'.  Let's consider that the end
     834                 :             :        of line is the end of the data in the cache.  The next
     835                 :             :        invocation of get_next_line will either read more data from the
     836                 :             :        underlying file or return false early because we've reached the
     837                 :             :        end of the file.  */
     838                 :          72 :     m_line_start_idx = m_nb_read;
     839                 :             : 
     840                 :    27581153 :   *line_len = len;
     841                 :             : 
     842                 :    27581153 :   return true;
     843                 :             : }
     844                 :             : 
     845                 :             : /* Consume the next bytes coming from the cache (or from its
     846                 :             :    underlying file if there are remaining unread bytes in the file)
     847                 :             :    until we reach the next end-of-line (or end-of-file).  There is no
     848                 :             :    copying from the cache involved.  Return TRUE upon successful
     849                 :             :    completion.  */
     850                 :             : 
     851                 :             : bool
     852                 :    27056957 : file_cache_slot::goto_next_line ()
     853                 :             : {
     854                 :    27056957 :   char *l;
     855                 :    27056957 :   ssize_t len;
     856                 :             : 
     857                 :    27056957 :   return get_next_line (&l, &len);
     858                 :             : }
     859                 :             : 
     860                 :             : /* Read an arbitrary line number LINE_NUM from the file cached in C.
     861                 :             :    If the line was read successfully, *LINE points to the beginning
     862                 :             :    of the line in the file cache and *LINE_LEN is the length of the
     863                 :             :    line.  *LINE is not nul-terminated, but may contain zero bytes.
     864                 :             :    *LINE is only valid until the next call of read_line_num.
     865                 :             :    This function returns bool if a line was read.  */
     866                 :             : 
     867                 :             : bool
     868                 :     1677793 : file_cache_slot::read_line_num (size_t line_num,
     869                 :             :                                 char ** line, ssize_t *line_len)
     870                 :             : {
     871                 :     1677793 :   gcc_assert (line_num > 0);
     872                 :             : 
     873                 :             :   /* Is the line in the recent line cache?
     874                 :             :      This assumes the main file processing is only using
     875                 :             :      a single contiguous cursor with only temporary excursions.  */
     876                 :     1677793 :   if (m_line_recent_first != m_line_recent_last
     877                 :     1590796 :         && m_line_recent[m_line_recent_first].line_num <= line_num
     878                 :     3214144 :         && m_line_recent[m_line_recent_last].line_num >= line_num)
     879                 :             :     {
     880                 :     1149150 :       line_info &last = m_line_recent[m_line_recent_last];
     881                 :     1149150 :       size_t mask = (1U << recent_cached_lines_shift) - 1;
     882                 :     1149150 :       size_t idx = (m_line_recent_last - (last.line_num - line_num)) & mask;
     883                 :     1149150 :       line_info &recent = m_line_recent[idx];
     884                 :     1149150 :       gcc_assert (recent.line_num == line_num);
     885                 :     1149150 :       *line = m_data + recent.start_pos;
     886                 :     1149150 :       *line_len = recent.end_pos - recent.start_pos;
     887                 :     1149150 :       return true;
     888                 :             :     }
     889                 :             : 
     890                 :      528643 :   if (line_num <= m_line_num)
     891                 :             :     {
     892                 :       33994 :       line_info l (line_num, 0, 0);
     893                 :       33994 :       int i = m_line_record.lower_bound (l, line_info::less_than);
     894                 :       33994 :       if (i == 0)
     895                 :             :         {
     896                 :       25593 :           m_line_start_idx = 0;
     897                 :       25593 :           m_line_num = 0;
     898                 :             :         }
     899                 :        8401 :       else if (m_line_record[i - 1].line_num == line_num)
     900                 :             :         {
     901                 :             :           /* We have the start/end of the line.  */
     902                 :           0 :           *line = m_data + m_line_record[i - 1].start_pos;
     903                 :           0 :           *line_len = m_line_record[i - 1].end_pos - m_line_record[i - 1].start_pos;
     904                 :           0 :           return true;
     905                 :             :         }
     906                 :             :       else
     907                 :             :        {
     908                 :        8401 :          gcc_assert (m_line_record[i - 1].line_num < m_line_num);
     909                 :        8401 :          m_line_start_idx = m_line_record[i - 1].start_pos;
     910                 :        8401 :          m_line_num = m_line_record[i - 1].line_num - 1;
     911                 :             :        }
     912                 :             :     }
     913                 :             : 
     914                 :             :   /*  Let's walk from line m_line_num up to line_num - 1, without
     915                 :             :       copying any line.  */
     916                 :    27584150 :   while (m_line_num < line_num - 1)
     917                 :    27056957 :     if (!goto_next_line ())
     918                 :             :       return false;
     919                 :             : 
     920                 :             :   /* The line we want is the next one.  Let's read it.  */
     921                 :      527193 :   return get_next_line (line, line_len);
     922                 :             : }
     923                 :             : 
     924                 :             : /* Return the physical source line that corresponds to FILE_PATH/LINE.
     925                 :             :    The line is not nul-terminated.  The returned pointer is only
     926                 :             :    valid until the next call of location_get_source_line.
     927                 :             :    Note that the line can contain several null characters,
     928                 :             :    so the returned value's length has the actual length of the line.
     929                 :             :    If the function fails, a NULL char_span is returned.  */
     930                 :             : 
     931                 :             : char_span
     932                 :     1708199 : file_cache::get_source_line (const char *file_path, int line)
     933                 :             : {
     934                 :     1708199 :   char *buffer = NULL;
     935                 :     1708199 :   ssize_t len;
     936                 :             : 
     937                 :     1708199 :   if (line == 0)
     938                 :          67 :     return char_span (NULL, 0);
     939                 :             : 
     940                 :     1708132 :   if (file_path == NULL)
     941                 :           0 :     return char_span (NULL, 0);
     942                 :             : 
     943                 :     1708132 :   file_cache_slot *c = lookup_or_add_file (file_path);
     944                 :     1708132 :   if (c == NULL)
     945                 :       30339 :     return char_span (NULL, 0);
     946                 :             : 
     947                 :     1677793 :   bool read = c->read_line_num (line, &buffer, &len);
     948                 :     1677793 :   if (!read)
     949                 :        4577 :     return char_span (NULL, 0);
     950                 :             : 
     951                 :     1673216 :   return char_span (buffer, len);
     952                 :             : }
     953                 :             : 
     954                 :             : char_span
     955                 :         229 : file_cache::get_source_file_content (const char *file_path)
     956                 :             : {
     957                 :         229 :   file_cache_slot *c = lookup_or_add_file (file_path);
     958                 :         229 :   if (c == nullptr)
     959                 :           4 :     return char_span (nullptr, 0);
     960                 :         225 :   return c->get_full_file_content ();
     961                 :             : }
     962                 :             : 
     963                 :             : #if CHECKING_P
     964                 :             : 
     965                 :             : namespace selftest {
     966                 :             : 
     967                 :             :  using temp_source_file = ::selftest::temp_source_file;
     968                 :             : 
     969                 :             : /* Verify reading of a specific line LINENUM in TMP, FC.  */
     970                 :             : 
     971                 :             : static void
     972                 :       18600 : check_line (temp_source_file &tmp, file_cache &fc, int linenum)
     973                 :             : {
     974                 :       18600 :   char_span line = fc.get_source_line (tmp.get_filename (), linenum);
     975                 :       18600 :   int n;
     976                 :       18600 :   const char *b = line.get_buffer ();
     977                 :       18600 :   size_t l = line.length ();
     978                 :       18600 :   char buf[5];
     979                 :       18600 :   ASSERT_LT (l, 5);
     980                 :       18600 :   memcpy (buf, b, l);
     981                 :       18600 :   buf[l] = '\0';
     982                 :       18600 :   ASSERT_TRUE (sscanf (buf, "%d", &n) == 1);
     983                 :       18600 :   ASSERT_EQ (n, linenum);
     984                 :       18600 : }
     985                 :             : 
     986                 :             : /* Test file cache replacement.  */
     987                 :             : 
     988                 :             : static void
     989                 :           4 : test_replacement ()
     990                 :             : {
     991                 :           4 :   const int maxline = 1000;
     992                 :             : 
     993                 :           4 :   char *vec = XNEWVEC (char, maxline * 5);
     994                 :           4 :   char *p = vec;
     995                 :           4 :   int i;
     996                 :        4008 :   for (i = 1; i <= maxline; i++)
     997                 :        4000 :     p += sprintf (p, "%d\n", i);
     998                 :             : 
     999                 :           4 :   temp_source_file tmp (SELFTEST_LOCATION, ".txt", vec);
    1000                 :           4 :   free (vec);
    1001                 :           4 :   file_cache fc;
    1002                 :             : 
    1003                 :        4004 :   for (i = 2; i <= maxline; i++)
    1004                 :             :     {
    1005                 :        3996 :       check_line (tmp, fc, i);
    1006                 :        3996 :       check_line (tmp, fc, i - 1);
    1007                 :        3996 :       if (i >= 10)
    1008                 :        3964 :         check_line (tmp, fc, i - 9);
    1009                 :        3964 :       if (i >= 350) /* Exceed the look behind cache.  */
    1010                 :        2604 :         check_line (tmp, fc, i - 300);
    1011                 :             :     }
    1012                 :          44 :   for (i = 5; i <= maxline; i += 100)
    1013                 :          40 :     check_line (tmp, fc, i);
    1014                 :        4004 :   for (i = 1; i <= maxline; i++)
    1015                 :        4000 :     check_line (tmp, fc, i);
    1016                 :           4 : }
    1017                 :             : 
    1018                 :             : /* Verify reading of input files (e.g. for caret-based diagnostics).  */
    1019                 :             : 
    1020                 :             : static void
    1021                 :           4 : test_reading_source_line ()
    1022                 :             : {
    1023                 :             :   /* Create a tempfile and write some text to it.  */
    1024                 :           4 :   temp_source_file tmp (SELFTEST_LOCATION, ".txt",
    1025                 :             :                         "01234567890123456789\n"
    1026                 :             :                         "This is the test text\n"
    1027                 :           4 :                         "This is the 3rd line");
    1028                 :           4 :   file_cache fc;
    1029                 :             : 
    1030                 :             :   /* Read back a specific line from the tempfile.  */
    1031                 :           4 :   char_span source_line = fc.get_source_line (tmp.get_filename (), 3);
    1032                 :           4 :   ASSERT_TRUE (source_line);
    1033                 :           4 :   ASSERT_TRUE (source_line.get_buffer () != NULL);
    1034                 :           4 :   ASSERT_EQ (20, source_line.length ());
    1035                 :           4 :   ASSERT_TRUE (!strncmp ("This is the 3rd line",
    1036                 :             :                          source_line.get_buffer (), source_line.length ()));
    1037                 :             : 
    1038                 :           4 :   source_line = fc.get_source_line (tmp.get_filename (), 2);
    1039                 :           4 :   ASSERT_TRUE (source_line);
    1040                 :           4 :   ASSERT_TRUE (source_line.get_buffer () != NULL);
    1041                 :           4 :   ASSERT_EQ (21, source_line.length ());
    1042                 :           4 :   ASSERT_TRUE (!strncmp ("This is the test text",
    1043                 :             :                          source_line.get_buffer (), source_line.length ()));
    1044                 :             : 
    1045                 :           4 :   source_line = fc.get_source_line (tmp.get_filename (), 4);
    1046                 :           4 :   ASSERT_FALSE (source_line);
    1047                 :           4 :   ASSERT_TRUE (source_line.get_buffer () == NULL);
    1048                 :           4 : }
    1049                 :             : 
    1050                 :             : /* Verify reading from buffers (e.g. for sarif-replay).  */
    1051                 :             : 
    1052                 :             : static void
    1053                 :           4 : test_reading_source_buffer ()
    1054                 :             : {
    1055                 :           4 :   const char *text = ("01234567890123456789\n"
    1056                 :             :                       "This is the test text\n"
    1057                 :             :                       "This is the 3rd line");
    1058                 :           4 :   const char *filename = "foo.txt";
    1059                 :           4 :   file_cache fc;
    1060                 :           4 :   fc.add_buffered_content (filename, text, strlen (text));
    1061                 :             : 
    1062                 :             :   /* Read back a specific line from the tempfile.  */
    1063                 :           4 :   char_span source_line = fc.get_source_line (filename, 3);
    1064                 :           4 :   ASSERT_TRUE (source_line);
    1065                 :           4 :   ASSERT_TRUE (source_line.get_buffer () != NULL);
    1066                 :           4 :   ASSERT_EQ (20, source_line.length ());
    1067                 :           4 :   ASSERT_TRUE (!strncmp ("This is the 3rd line",
    1068                 :             :                          source_line.get_buffer (), source_line.length ()));
    1069                 :             : 
    1070                 :           4 :   source_line = fc.get_source_line (filename, 2);
    1071                 :           4 :   ASSERT_TRUE (source_line);
    1072                 :           4 :   ASSERT_TRUE (source_line.get_buffer () != NULL);
    1073                 :           4 :   ASSERT_EQ (21, source_line.length ());
    1074                 :           4 :   ASSERT_TRUE (!strncmp ("This is the test text",
    1075                 :             :                          source_line.get_buffer (), source_line.length ()));
    1076                 :             : 
    1077                 :           4 :   source_line = fc.get_source_line (filename, 4);
    1078                 :           4 :   ASSERT_FALSE (source_line);
    1079                 :           4 :   ASSERT_TRUE (source_line.get_buffer () == NULL);
    1080                 :           4 : }
    1081                 :             : 
    1082                 :             : /* Run all of the selftests within this file.  */
    1083                 :             : 
    1084                 :             : void
    1085                 :           4 : file_cache_cc_tests ()
    1086                 :             : {
    1087                 :           4 :   test_reading_source_line ();
    1088                 :           4 :   test_reading_source_buffer ();
    1089                 :           4 :   test_replacement ();
    1090                 :           4 : }
    1091                 :             : 
    1092                 :             : } // namespace selftest
    1093                 :             : 
    1094                 :             : #endif /* CHECKING_P */
    1095                 :             : 
    1096                 :             : } // namespace diagnostics
        

Generated by: LCOV version 2.1-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.